chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
|
||||
# with full pathname. RELATIVE may makes it easier to extract an executable name
|
||||
# automatically.
|
||||
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
|
||||
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
|
||||
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
|
||||
foreach( testsourcefile ${APP_SOURCES} )
|
||||
# I used a simple string replace, to cut off .cpp.
|
||||
string( REPLACE ".cpp" "" testname ${testsourcefile} )
|
||||
add_executable( ${testname} ${testsourcefile} )
|
||||
|
||||
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
|
||||
if(OpenMP_CXX_FOUND)
|
||||
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
|
||||
endif()
|
||||
install(TARGETS ${testname} DESTINATION "bin/data_structures")
|
||||
|
||||
endforeach( testsourcefile ${APP_SOURCES} )
|
||||
|
||||
add_subdirectory(cll)
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief A simple tree implementation using nodes
|
||||
*
|
||||
* \todo update code to use C++ STL library features and OO structure
|
||||
* \warning This program is a poor implementation and does not utilize any of
|
||||
* the C++ STL features.
|
||||
*/
|
||||
#include <algorithm> /// for std::max
|
||||
#include <iostream> /// for std::cout
|
||||
#include <queue> /// for std::queue
|
||||
|
||||
using node = struct node {
|
||||
int data;
|
||||
int height;
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief creates and returns a new node
|
||||
* @param[in] data value stored in the node
|
||||
* @return newly created node
|
||||
*/
|
||||
node *createNode(int data) {
|
||||
node *nn = new node();
|
||||
nn->data = data;
|
||||
nn->height = 0;
|
||||
nn->left = nullptr;
|
||||
nn->right = nullptr;
|
||||
return nn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[in] root the root of the tree
|
||||
* @return height of tree
|
||||
*/
|
||||
int height(node *root) {
|
||||
if (root == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return 1 + std::max(height(root->left), height(root->right));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[in] root of the tree
|
||||
* @return difference between height of left and right subtree
|
||||
*/
|
||||
int getBalance(node *root) { return height(root->left) - height(root->right); }
|
||||
|
||||
/**
|
||||
* @param root of the tree to be rotated
|
||||
* @return node after right rotation
|
||||
*/
|
||||
node *rightRotate(node *root) {
|
||||
node *t = root->left;
|
||||
node *u = t->right;
|
||||
t->right = root;
|
||||
root->left = u;
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param root of the tree to be rotated
|
||||
* @return node after left rotation
|
||||
*/
|
||||
node *leftRotate(node *root) {
|
||||
node *t = root->right;
|
||||
node *u = t->left;
|
||||
t->left = root;
|
||||
root->right = u;
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param root of the tree
|
||||
* @returns node with minimum value in the tree
|
||||
*/
|
||||
node *minValue(node *root) {
|
||||
if (root->left == nullptr) {
|
||||
return root;
|
||||
}
|
||||
return minValue(root->left);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief inserts a new element into AVL tree
|
||||
* @param root of the tree
|
||||
* @param[in] item the element to be insterted into the tree
|
||||
* @return root of the updated tree
|
||||
*/
|
||||
node *insert(node *root, int item) {
|
||||
if (root == nullptr) {
|
||||
return createNode(item);
|
||||
}
|
||||
if (item < root->data) {
|
||||
root->left = insert(root->left, item);
|
||||
} else {
|
||||
root->right = insert(root->right, item);
|
||||
}
|
||||
int b = getBalance(root);
|
||||
if (b > 1) {
|
||||
if (getBalance(root->left) < 0) {
|
||||
root->left = leftRotate(root->left); // Left-Right Case
|
||||
}
|
||||
return rightRotate(root); // Left-Left Case
|
||||
} else if (b < -1) {
|
||||
if (getBalance(root->right) > 0) {
|
||||
root->right = rightRotate(root->right); // Right-Left Case
|
||||
}
|
||||
return leftRotate(root); // Right-Right Case
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes a given element from AVL tree
|
||||
* @param root of the tree
|
||||
* @param[in] element the element to be deleted from the tree
|
||||
* @return root of the updated tree
|
||||
*/
|
||||
node *deleteNode(node *root, int element) {
|
||||
if (root == nullptr) {
|
||||
return root;
|
||||
}
|
||||
if (element < root->data) {
|
||||
root->left = deleteNode(root->left, element);
|
||||
} else if (element > root->data) {
|
||||
root->right = deleteNode(root->right, element);
|
||||
|
||||
} else {
|
||||
// Node to be deleted is leaf node or have only one Child
|
||||
if (!root->right || !root->left) {
|
||||
node *temp = !root->right ? root->left : root->right;
|
||||
delete root;
|
||||
return temp;
|
||||
}
|
||||
// Node to be deleted have both left and right subtrees
|
||||
node *temp = minValue(root->right);
|
||||
root->data = temp->data;
|
||||
root->right = deleteNode(root->right, temp->data);
|
||||
}
|
||||
// Balancing Tree after deletion
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief calls delete on every node
|
||||
* @param root of the tree
|
||||
*/
|
||||
void deleteAllNodes(const node *const root) {
|
||||
if (root) {
|
||||
deleteAllNodes(root->left);
|
||||
deleteAllNodes(root->right);
|
||||
delete root;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief prints given tree in the LevelOrder
|
||||
* @param[in] root of the tree
|
||||
*/
|
||||
void levelOrder(node *root) {
|
||||
std::queue<node *> q;
|
||||
q.push(root);
|
||||
while (!q.empty()) {
|
||||
root = q.front();
|
||||
std::cout << root->data << " ";
|
||||
q.pop();
|
||||
if (root->left) {
|
||||
q.push(root->left);
|
||||
}
|
||||
if (root->right) {
|
||||
q.push(root->right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
// Testing AVL Tree
|
||||
node *root = nullptr;
|
||||
int i = 0;
|
||||
for (i = 1; i <= 7; i++) root = insert(root, i);
|
||||
std::cout << "LevelOrder: ";
|
||||
levelOrder(root);
|
||||
root = deleteNode(root, 1); // Deleting key with value 1
|
||||
std::cout << "\nLevelOrder: ";
|
||||
levelOrder(root);
|
||||
root = deleteNode(root, 4); // Deletin key with value 4
|
||||
std::cout << "\nLevelOrder: ";
|
||||
levelOrder(root);
|
||||
deleteAllNodes(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief A simple tree implementation using structured nodes
|
||||
*
|
||||
* \todo update code to use C++ STL library features and OO structure
|
||||
* \warning This program is a poor implementation - C style - and does not
|
||||
* utilize any of the C++ STL features.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
struct node {
|
||||
int val;
|
||||
node *left;
|
||||
node *right;
|
||||
};
|
||||
|
||||
struct Queue {
|
||||
node *t[100];
|
||||
int front;
|
||||
int rear;
|
||||
};
|
||||
|
||||
Queue queue;
|
||||
|
||||
void enqueue(node *n) { queue.t[queue.rear++] = n; }
|
||||
|
||||
node *dequeue() { return (queue.t[queue.front++]); }
|
||||
|
||||
void Insert(node *n, int x) {
|
||||
if (x < n->val) {
|
||||
if (n->left == NULL) {
|
||||
node *temp = new node;
|
||||
temp->val = x;
|
||||
temp->left = NULL;
|
||||
temp->right = NULL;
|
||||
n->left = temp;
|
||||
} else {
|
||||
Insert(n->left, x);
|
||||
}
|
||||
} else {
|
||||
if (n->right == NULL) {
|
||||
node *temp = new node;
|
||||
temp->val = x;
|
||||
temp->left = NULL;
|
||||
temp->right = NULL;
|
||||
n->right = temp;
|
||||
} else {
|
||||
Insert(n->right, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int findMaxInLeftST(node *n) {
|
||||
while (n->right != NULL) {
|
||||
n = n->right;
|
||||
}
|
||||
return n->val;
|
||||
}
|
||||
|
||||
void Remove(node *p, node *n, int x) {
|
||||
if (n->val == x) {
|
||||
if (n->right == NULL && n->left == NULL) {
|
||||
if (x < p->val) {
|
||||
p->right = NULL;
|
||||
} else {
|
||||
p->left = NULL;
|
||||
}
|
||||
} else if (n->right == NULL) {
|
||||
if (x < p->val) {
|
||||
p->right = n->left;
|
||||
} else {
|
||||
p->left = n->left;
|
||||
}
|
||||
} else if (n->left == NULL) {
|
||||
if (x < p->val) {
|
||||
p->right = n->right;
|
||||
} else {
|
||||
p->left = n->right;
|
||||
}
|
||||
} else {
|
||||
int y = findMaxInLeftST(n->left);
|
||||
n->val = y;
|
||||
Remove(n, n->right, y);
|
||||
}
|
||||
} else if (x < n->val) {
|
||||
Remove(n, n->left, x);
|
||||
} else {
|
||||
Remove(n, n->right, x);
|
||||
}
|
||||
}
|
||||
|
||||
void BFT(node *n) {
|
||||
if (n != NULL) {
|
||||
std::cout << n->val << " ";
|
||||
enqueue(n->left);
|
||||
enqueue(n->right);
|
||||
BFT(dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
void Pre(node *n) {
|
||||
if (n != NULL) {
|
||||
std::cout << n->val << " ";
|
||||
Pre(n->left);
|
||||
Pre(n->right);
|
||||
}
|
||||
}
|
||||
|
||||
void In(node *n) {
|
||||
if (n != NULL) {
|
||||
In(n->left);
|
||||
std::cout << n->val << " ";
|
||||
In(n->right);
|
||||
}
|
||||
}
|
||||
|
||||
void Post(node *n) {
|
||||
if (n != NULL) {
|
||||
Post(n->left);
|
||||
Post(n->right);
|
||||
std::cout << n->val << " ";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
queue.front = 0;
|
||||
queue.rear = 0;
|
||||
int value;
|
||||
int ch;
|
||||
node *root = new node;
|
||||
std::cout << "\nEnter the value of root node :";
|
||||
std::cin >> value;
|
||||
root->val = value;
|
||||
root->left = NULL;
|
||||
root->right = NULL;
|
||||
do {
|
||||
std::cout << "\n1. Insert"
|
||||
<< "\n2. Delete"
|
||||
<< "\n3. Breadth First"
|
||||
<< "\n4. Preorder Depth First"
|
||||
<< "\n5. Inorder Depth First"
|
||||
<< "\n6. Postorder Depth First";
|
||||
|
||||
std::cout << "\nEnter Your Choice : ";
|
||||
std::cin >> ch;
|
||||
int x;
|
||||
switch (ch) {
|
||||
case 1:
|
||||
std::cout << "\nEnter the value to be Inserted : ";
|
||||
std::cin >> x;
|
||||
Insert(root, x);
|
||||
break;
|
||||
case 2:
|
||||
std::cout << "\nEnter the value to be Deleted : ";
|
||||
std::cin >> x;
|
||||
Remove(root, root, x);
|
||||
break;
|
||||
case 3:
|
||||
BFT(root);
|
||||
break;
|
||||
case 4:
|
||||
Pre(root);
|
||||
break;
|
||||
case 5:
|
||||
In(root);
|
||||
break;
|
||||
case 6:
|
||||
Post(root);
|
||||
break;
|
||||
}
|
||||
} while (ch != 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A generic [binary search tree](https://en.wikipedia.org/wiki/Binary_search_tree) implementation.
|
||||
* Here you can find more information about the algorithm: [Scaler - Binary Search tree](https://www.scaler.com/topics/data-structures/binary-search-tree/).
|
||||
* @see binary_search_tree.cpp
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief The Binary Search Tree class.
|
||||
*
|
||||
* @tparam T The type of the binary search tree key.
|
||||
*/
|
||||
template <class T>
|
||||
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<bst_node> left; /**< Pointer to left subtree. */
|
||||
std::unique_ptr<bst_node> 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<bst_node> 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<bst_node>& 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<bst_node>& 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<bst_node>& node, T new_value) {
|
||||
if (root_ == node && !root_) {
|
||||
root_ = std::unique_ptr<bst_node>(new bst_node(new_value));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (new_value < node->value) {
|
||||
if (!node->left) {
|
||||
node->left = std::unique_ptr<bst_node>(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<bst_node>(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<bst_node>& parent,
|
||||
std::unique_ptr<bst_node>& 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<bst_node>& 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<bst_node>& 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<void(T)> callback,
|
||||
std::unique_ptr<bst_node>& 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<void(T)> callback,
|
||||
std::unique_ptr<bst_node>& 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<void(T)> callback,
|
||||
std::unique_ptr<bst_node>& 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<T> List of values, sorted in in-order order.
|
||||
*/
|
||||
std::vector<T> get_elements_inorder() {
|
||||
std::vector<T> 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<T> List of values, sorted in pre-order order.
|
||||
*/
|
||||
std::vector<T> get_elements_preorder() {
|
||||
std::vector<T> 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<T> List of values, sorted in post-order order.
|
||||
*/
|
||||
std::vector<T> get_elements_postorder() {
|
||||
std::vector<T> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> tree;
|
||||
tree.insert(5);
|
||||
tree.insert(4);
|
||||
tree.insert(3);
|
||||
tree.insert(6);
|
||||
|
||||
std::vector<int> expected = {3, 4, 5, 6};
|
||||
std::vector<int> 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<int> tree;
|
||||
tree.insert(5);
|
||||
tree.insert(4);
|
||||
tree.insert(3);
|
||||
tree.insert(6);
|
||||
|
||||
std::vector<int> expected = {5, 4, 3, 6};
|
||||
std::vector<int> 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<int> tree;
|
||||
tree.insert(5);
|
||||
tree.insert(4);
|
||||
tree.insert(3);
|
||||
tree.insert(6);
|
||||
|
||||
std::vector<int> expected = {3, 4, 6, 5};
|
||||
std::vector<int> 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();
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief A C++ program to demonstrate common Binary Heap Operations
|
||||
*/
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
/** A class for Min Heap */
|
||||
class MinHeap {
|
||||
int *harr; ///< pointer to array of elements in heap
|
||||
int capacity; ///< maximum possible size of min heap
|
||||
int heap_size; ///< Current number of elements in min heap
|
||||
|
||||
public:
|
||||
/** Constructor: Builds a heap from a given array a[] of given size
|
||||
* \param[in] capacity initial heap capacity
|
||||
*/
|
||||
explicit MinHeap(int cap) {
|
||||
heap_size = 0;
|
||||
capacity = cap;
|
||||
harr = new int[cap];
|
||||
}
|
||||
|
||||
/** to heapify a subtree with the root at given index */
|
||||
void MinHeapify(int);
|
||||
|
||||
int parent(int i) { return (i - 1) / 2; }
|
||||
|
||||
/** to get index of left child of node at index i */
|
||||
int left(int i) { return (2 * i + 1); }
|
||||
|
||||
/** to get index of right child of node at index i */
|
||||
int right(int i) { return (2 * i + 2); }
|
||||
|
||||
/** to extract the root which is the minimum element */
|
||||
int extractMin();
|
||||
|
||||
/** Decreases key value of key at index i to new_val */
|
||||
void decreaseKey(int i, int new_val);
|
||||
|
||||
/** Returns the minimum key (key at root) from min heap */
|
||||
int getMin() { return harr[0]; }
|
||||
|
||||
/** Deletes a key stored at index i */
|
||||
void deleteKey(int i);
|
||||
|
||||
/** Inserts a new key 'k' */
|
||||
void insertKey(int k);
|
||||
|
||||
~MinHeap() { delete[] harr; }
|
||||
};
|
||||
|
||||
// Inserts a new key 'k'
|
||||
void MinHeap::insertKey(int k) {
|
||||
if (heap_size == capacity) {
|
||||
std::cout << "\nOverflow: Could not insertKey\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// First insert the new key at the end
|
||||
heap_size++;
|
||||
int i = heap_size - 1;
|
||||
harr[i] = k;
|
||||
|
||||
// Fix the min heap property if it is violated
|
||||
while (i != 0 && harr[parent(i)] > harr[i]) {
|
||||
std::swap(harr[i], harr[parent(i)]);
|
||||
i = parent(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Decreases value of key at index 'i' to new_val. It is assumed that new_val
|
||||
* is smaller than harr[i].
|
||||
*/
|
||||
void MinHeap::decreaseKey(int i, int new_val) {
|
||||
harr[i] = new_val;
|
||||
while (i != 0 && harr[parent(i)] > harr[i]) {
|
||||
std::swap(harr[i], harr[parent(i)]);
|
||||
i = parent(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Method to remove minimum element (or root) from min heap
|
||||
int MinHeap::extractMin() {
|
||||
if (heap_size <= 0)
|
||||
return INT_MAX;
|
||||
if (heap_size == 1) {
|
||||
heap_size--;
|
||||
return harr[0];
|
||||
}
|
||||
|
||||
// Store the minimum value, and remove it from heap
|
||||
int root = harr[0];
|
||||
harr[0] = harr[heap_size - 1];
|
||||
heap_size--;
|
||||
MinHeapify(0);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/** This function deletes key at index i. It first reduced value to minus
|
||||
* infinite, then calls extractMin()
|
||||
*/
|
||||
void MinHeap::deleteKey(int i) {
|
||||
decreaseKey(i, INT_MIN);
|
||||
extractMin();
|
||||
}
|
||||
|
||||
/** A recursive method to heapify a subtree with the root at given index
|
||||
* This method assumes that the subtrees are already heapified
|
||||
*/
|
||||
void MinHeap::MinHeapify(int i) {
|
||||
int l = left(i);
|
||||
int r = right(i);
|
||||
int smallest = i;
|
||||
if (l < heap_size && harr[l] < harr[i])
|
||||
smallest = l;
|
||||
if (r < heap_size && harr[r] < harr[smallest])
|
||||
smallest = r;
|
||||
if (smallest != i) {
|
||||
std::swap(harr[i], harr[smallest]);
|
||||
MinHeapify(smallest);
|
||||
}
|
||||
}
|
||||
|
||||
// Driver program to test above functions
|
||||
int main() {
|
||||
MinHeap h(11);
|
||||
h.insertKey(3);
|
||||
h.insertKey(2);
|
||||
h.deleteKey(1);
|
||||
h.insertKey(15);
|
||||
h.insertKey(5);
|
||||
h.insertKey(4);
|
||||
h.insertKey(45);
|
||||
std::cout << h.extractMin() << " ";
|
||||
std::cout << h.getMin() << " ";
|
||||
h.decreaseKey(2, 1);
|
||||
std::cout << h.getMin();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter)
|
||||
* generic implementation in C++
|
||||
* @details A Bloom filter is a space-efficient probabilistic data structure,
|
||||
* a query returns either "possibly in set" or "definitely not in set".
|
||||
*
|
||||
* More generally, fewer than 10 bits per element are required for a 1% false
|
||||
* positive probability, independent of the size or number of elements in the
|
||||
* set.
|
||||
*
|
||||
* It helps us to not make an "expensive operations", like disk IO - we can
|
||||
* use bloom filter to check incoming request, and with a good probability
|
||||
* get an answer of bloom filter, that we don't need to make our "expensive
|
||||
* operation"
|
||||
*
|
||||
*
|
||||
* [Very good use case example](https://stackoverflow.com/a/30247022)
|
||||
*
|
||||
* Basic bloom filter doesn't support deleting of elements, so
|
||||
* we don't need to implement deletion in bloom filter and bitset in our case.
|
||||
* @author [DanArmor](https://github.com/DanArmor)
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <functional> /// for list of hash functions for bloom filter constructor
|
||||
#include <initializer_list> /// for initializer_list for bloom filter constructor
|
||||
#include <string> /// for testing on strings
|
||||
#include <vector> /// for std::vector
|
||||
#include <iostream> /// for IO operations
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data Structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @brief Simple bitset implementation for bloom filter
|
||||
*/
|
||||
class Bitset {
|
||||
private:
|
||||
std::vector<std::size_t> data; ///< short info of this variable
|
||||
static const std::size_t blockSize =
|
||||
sizeof(std::size_t); ///< size of integer type, that we are using in
|
||||
///< our bitset
|
||||
public:
|
||||
explicit Bitset(std::size_t);
|
||||
std::size_t size();
|
||||
void add(std::size_t);
|
||||
bool contains(std::size_t);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility function to return the size of the inner array
|
||||
* @returns the size of inner array
|
||||
*/
|
||||
std::size_t Bitset::size() { return data.size(); }
|
||||
|
||||
/**
|
||||
* @brief BitSet class constructor
|
||||
* @param initSize amount of blocks, each contain sizeof(std::size_t) bits
|
||||
*/
|
||||
Bitset::Bitset(std::size_t initSize) : data(initSize) {}
|
||||
|
||||
/**
|
||||
* @brief Turn bit on position x to 1s
|
||||
*
|
||||
* @param x position to turn bit on
|
||||
* @returns void
|
||||
*/
|
||||
void Bitset::add(std::size_t x) {
|
||||
std::size_t blockIndex = x / blockSize;
|
||||
if (blockIndex >= data.size()) {
|
||||
data.resize(blockIndex + 1);
|
||||
}
|
||||
data[blockIndex] |= 1 << (x % blockSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Doest bitset contains element x
|
||||
*
|
||||
* @param x position in bitset to check
|
||||
* @returns true if bit position x is 1
|
||||
* @returns false if bit position x is 0
|
||||
*/
|
||||
bool Bitset::contains(std::size_t x) {
|
||||
std::size_t blockIndex = x / blockSize;
|
||||
if (blockIndex >= data.size()) {
|
||||
return false;
|
||||
}
|
||||
return data[blockIndex] & (1 << (x % blockSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bloom filter template class
|
||||
* @tparam T type of elements that we need to filter
|
||||
*/
|
||||
template <typename T>
|
||||
class BloomFilter {
|
||||
private:
|
||||
Bitset set; ///< inner bitset for elements
|
||||
std::vector<std::function<std::size_t(T)>>
|
||||
hashFunks; ///< hash functions for T type
|
||||
|
||||
public:
|
||||
BloomFilter(std::size_t,
|
||||
std::initializer_list<std::function<std::size_t(T)>>);
|
||||
void add(T);
|
||||
bool contains(T);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor for Bloom filter
|
||||
*
|
||||
* @tparam T type of elements that we need to filter
|
||||
* @param size initial size of Bloom filter
|
||||
* @param funks hash functions for T type
|
||||
* @returns none
|
||||
*/
|
||||
template <typename T>
|
||||
BloomFilter<T>::BloomFilter(
|
||||
std::size_t size,
|
||||
std::initializer_list<std::function<std::size_t(T)>> funks)
|
||||
: set(size), hashFunks(funks) {}
|
||||
|
||||
/**
|
||||
* @brief Add function for Bloom filter
|
||||
*
|
||||
* @tparam T type of elements that we need to filter
|
||||
* @param x element to add to filter
|
||||
* @returns void
|
||||
*/
|
||||
template <typename T>
|
||||
void BloomFilter<T>::add(T x) {
|
||||
for (std::size_t i = 0; i < hashFunks.size(); i++) {
|
||||
set.add(hashFunks[i](x) % (sizeof(std::size_t) * set.size()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check element function for Bloom filter
|
||||
*
|
||||
* @tparam T type of elements that we need to filter
|
||||
* @param x element to check in filter
|
||||
* @return true if the element probably appears in the filter
|
||||
* @return false if the element certainly does not appear in the filter
|
||||
*/
|
||||
template <typename T>
|
||||
bool BloomFilter<T>::contains(T x) {
|
||||
for (std::size_t i = 0; i < hashFunks.size(); i++) {
|
||||
if (set.contains(hashFunks[i](x) %
|
||||
(sizeof(std::size_t) * set.size())) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief [Function djb2](http://www.cse.yorku.ca/~oz/hash.html)
|
||||
* to get hash for the given string.
|
||||
*
|
||||
* @param s string to get hash from
|
||||
* @returns hash for a string
|
||||
*/
|
||||
static std::size_t hashDJB2(std::string const& s) {
|
||||
std::size_t hash = 5381;
|
||||
for (char c : s) {
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief [Hash
|
||||
* function](https://stackoverflow.com/questions/8317508/hash-function-for-a-string),
|
||||
* to get hash for the given string.
|
||||
*
|
||||
* @param s string to get hash from
|
||||
* @returns hash for the given string
|
||||
*/
|
||||
static std::size_t hashStr(std::string const& s) {
|
||||
std::size_t hash = 37;
|
||||
std::size_t primeNum1 = 54059;
|
||||
std::size_t primeNum2 = 76963;
|
||||
for (char c : s) {
|
||||
hash = (hash * primeNum1) ^ (c * primeNum2);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief [Hash function for
|
||||
* test](https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key)
|
||||
*
|
||||
* @param x to get hash from
|
||||
* @returns hash for the `x` parameter
|
||||
*/
|
||||
std::size_t hashInt_1(int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief [Hash function for
|
||||
* test](https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key)
|
||||
*
|
||||
* @param x to get hash from
|
||||
* @returns hash for the `x` parameter
|
||||
*/
|
||||
std::size_t hashInt_2(int x) {
|
||||
auto y = static_cast<std::size_t>(x);
|
||||
y = (y ^ (y >> 30)) * static_cast<std::size_t>(0xbf58476d1ce4e5b9);
|
||||
y = (y ^ (y >> 27)) * static_cast<std::size_t>(0x94d049bb133111eb);
|
||||
y = y ^ (y >> 31);
|
||||
return y;
|
||||
}
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Test for bloom filter with string as generic type
|
||||
* @returns void
|
||||
*/
|
||||
static void test_bloom_filter_string() {
|
||||
data_structures::BloomFilter<std::string> filter(
|
||||
10, {data_structures::hashDJB2, data_structures::hashStr});
|
||||
std::vector<std::string> toCheck{"hello", "world", "!"};
|
||||
std::vector<std::string> toFalse{"false", "world2", "!!!"};
|
||||
for (const auto& x : toCheck) {
|
||||
filter.add(x);
|
||||
}
|
||||
for (const auto& x : toFalse) {
|
||||
assert(filter.contains(x) == false);
|
||||
}
|
||||
for (const auto& x : toCheck) {
|
||||
assert(filter.contains(x));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test for bloom filter with int as generic type
|
||||
* @returns void
|
||||
*/
|
||||
static void test_bloom_filter_int() {
|
||||
data_structures::BloomFilter<int> filter(
|
||||
20, {data_structures::hashInt_1, data_structures::hashInt_2});
|
||||
std::vector<int> toCheck{100, 200, 300, 50};
|
||||
std::vector<int> toFalse{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
for (int x : toCheck) {
|
||||
filter.add(x);
|
||||
}
|
||||
for (int x : toFalse) {
|
||||
assert(filter.contains(x) == false);
|
||||
}
|
||||
for (int x : toCheck) {
|
||||
assert(filter.contains(x));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test for bitset
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
static void test_bitset() {
|
||||
data_structures::Bitset set(2);
|
||||
std::vector<std::size_t> toCheck{0, 1, 5, 8, 63, 64, 67, 127};
|
||||
for (auto x : toCheck) {
|
||||
set.add(x);
|
||||
assert(set.contains(x));
|
||||
}
|
||||
assert(set.contains(128) == false);
|
||||
assert(set.contains(256) == false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
// run self-test implementations
|
||||
|
||||
test_bitset(); // run test for bitset, because bloom filter is depending on it
|
||||
test_bloom_filter_string();
|
||||
test_bloom_filter_int();
|
||||
|
||||
std::cout << "All tests have successfully passed!\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <iostream>
|
||||
|
||||
struct node {
|
||||
int data;
|
||||
struct node* next;
|
||||
};
|
||||
class Queue {
|
||||
node* front = nullptr;
|
||||
node* rear = nullptr;
|
||||
|
||||
Queue(const Queue&) = delete;
|
||||
Queue& operator=(const Queue&) = delete;
|
||||
|
||||
public:
|
||||
Queue() = default;
|
||||
~Queue() {
|
||||
while (front) {
|
||||
dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void createNode(int val) {
|
||||
auto* nn = new node;
|
||||
nn->data = val;
|
||||
nn->next = nullptr;
|
||||
front = nn;
|
||||
rear = nn;
|
||||
}
|
||||
|
||||
public:
|
||||
void enqueue(int val) {
|
||||
if (front == nullptr || rear == nullptr) {
|
||||
createNode(val);
|
||||
} else {
|
||||
node* nn = new node;
|
||||
nn->data = val;
|
||||
rear->next = nn;
|
||||
nn->next = front;
|
||||
rear = nn;
|
||||
}
|
||||
}
|
||||
void dequeue() {
|
||||
if (front == nullptr) {
|
||||
return;
|
||||
}
|
||||
const node* const n = front;
|
||||
if (front == rear) {
|
||||
front = nullptr;
|
||||
rear = nullptr;
|
||||
} else {
|
||||
front = front->next;
|
||||
rear->next = front;
|
||||
}
|
||||
delete n;
|
||||
}
|
||||
void traverse() {
|
||||
if (front == nullptr) {
|
||||
return;
|
||||
}
|
||||
const node* ptr = front;
|
||||
do {
|
||||
std::cout << ptr->data << ' ';
|
||||
ptr = ptr->next;
|
||||
} while (ptr != front);
|
||||
std::cout << '\n';
|
||||
}
|
||||
};
|
||||
int main(void) {
|
||||
Queue q;
|
||||
q.enqueue(10);
|
||||
q.enqueue(20);
|
||||
q.enqueue(30);
|
||||
q.enqueue(40);
|
||||
q.enqueue(50);
|
||||
q.enqueue(60);
|
||||
q.enqueue(70);
|
||||
q.traverse();
|
||||
q.dequeue();
|
||||
q.traverse();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
add_executable( cll
|
||||
cll.cpp
|
||||
main_cll.cpp
|
||||
)
|
||||
install(TARGETS cll DESTINATION "bin/data_structures")
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
A simple class for Cicular Linear Linked List
|
||||
*/
|
||||
#include "cll.h"
|
||||
using namespace std;
|
||||
|
||||
/* Constructor */
|
||||
cll::cll() {
|
||||
head = NULL;
|
||||
total = 0;
|
||||
}
|
||||
|
||||
cll::~cll() { /* Desstructure, no need to fill */
|
||||
}
|
||||
|
||||
/* Display a list. and total element */
|
||||
void cll::display() {
|
||||
if (head == NULL)
|
||||
cout << "List is empty !" << endl;
|
||||
else {
|
||||
cout << "CLL list: ";
|
||||
node *current = head;
|
||||
for (int i = 0; i < total; i++) {
|
||||
cout << current->data << " -> ";
|
||||
current = current->next;
|
||||
}
|
||||
cout << head->data << endl;
|
||||
cout << "Total element: " << total << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* List insert a new value at head in list */
|
||||
void cll::insert_front(int new_data) {
|
||||
node *newNode;
|
||||
newNode = new node;
|
||||
newNode->data = new_data;
|
||||
newNode->next = NULL;
|
||||
if (head == NULL) {
|
||||
head = newNode;
|
||||
head->next = head;
|
||||
} else {
|
||||
node *current = head;
|
||||
while (current->next != head) {
|
||||
current = current->next;
|
||||
}
|
||||
newNode->next = head;
|
||||
current->next = newNode;
|
||||
head = newNode;
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
/* List insert a new value at head in list */
|
||||
void cll::insert_tail(int new_data) {
|
||||
node *newNode;
|
||||
newNode = new node;
|
||||
newNode->data = new_data;
|
||||
newNode->next = NULL;
|
||||
if (head == NULL) {
|
||||
head = newNode;
|
||||
head->next = head;
|
||||
} else {
|
||||
node *current = head;
|
||||
while (current->next != head) {
|
||||
current = current->next;
|
||||
}
|
||||
current->next = newNode;
|
||||
newNode->next = head;
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
/* Get total element in list */
|
||||
int cll::get_size() { return total; }
|
||||
|
||||
/* Return true if the requested item (sent in as an argument)
|
||||
is in the list, otherwise return false */
|
||||
bool cll::find_item(int item_to_find) {
|
||||
if (head == NULL) {
|
||||
cout << "List is empty !" << endl;
|
||||
return false;
|
||||
} else {
|
||||
node *current = head;
|
||||
while (current->next != head) {
|
||||
if (current->data == item_to_find)
|
||||
return true;
|
||||
current = current->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overloading method*/
|
||||
int cll::operator*() { return head->data; }
|
||||
|
||||
/* Overload the pre-increment operator.
|
||||
The iterator is advanced to the next node. */
|
||||
void cll::operator++() {
|
||||
if (head == NULL) {
|
||||
cout << "List is empty !" << endl;
|
||||
} else {
|
||||
node *current = head;
|
||||
while (current->next != head) {
|
||||
current = current->next;
|
||||
}
|
||||
current->next = head->next;
|
||||
head = head->next;
|
||||
}
|
||||
total--;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Simple data structure CLL (Circular Linear Linked List)
|
||||
* */
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef CLL_H
|
||||
#define CLL_H
|
||||
/*The data structure is a linear linked list of integers */
|
||||
struct node {
|
||||
int data;
|
||||
node* next;
|
||||
};
|
||||
|
||||
class cll {
|
||||
public:
|
||||
cll(); /* Construct without parameter */
|
||||
~cll();
|
||||
void display(); /* Show the list */
|
||||
|
||||
/******************************************************
|
||||
* Useful method for list
|
||||
*******************************************************/
|
||||
void insert_front(int new_data); /* Insert a new value at head */
|
||||
void insert_tail(int new_data); /* Insert a new value at tail */
|
||||
int get_size(); /* Get total element in list */
|
||||
bool find_item(int item_to_find); /* Find an item in list */
|
||||
|
||||
/******************************************************
|
||||
* Overloading method for list
|
||||
*******************************************************/
|
||||
int operator*(); /* Returns the info contained in head */
|
||||
/* Overload the pre-increment operator.
|
||||
The iterator is advanced to the next node. */
|
||||
void operator++();
|
||||
|
||||
protected:
|
||||
node* head;
|
||||
int total; /* Total element in a list */
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "cll.h"
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
/* Test CLL */
|
||||
cout << "----------- Test construct -----------" << endl;
|
||||
cll list1;
|
||||
list1.display();
|
||||
cout << "----------- Test insert front -----------" << endl;
|
||||
list1.insert_front(5);
|
||||
cout << "After insert 5 at front: " << endl;
|
||||
list1.display();
|
||||
cout << "After insert 10 3 7 at front: " << endl;
|
||||
list1.insert_front(10);
|
||||
list1.insert_front(3);
|
||||
list1.insert_front(7);
|
||||
list1.display();
|
||||
cout << "----------- Test insert tail -----------" << endl;
|
||||
cout << "After insert 18 19 20 at tail: " << endl;
|
||||
list1.insert_tail(18);
|
||||
list1.insert_tail(19);
|
||||
list1.insert_tail(20);
|
||||
list1.display();
|
||||
cout << "----------- Test find item -----------" << endl;
|
||||
if (list1.find_item(10))
|
||||
cout << "PASS" << endl;
|
||||
else
|
||||
cout << "FAIL" << endl;
|
||||
if (!list1.find_item(30))
|
||||
cout << "PASS" << endl;
|
||||
else
|
||||
cout << "FAIL" << endl;
|
||||
cout << "----------- Test * operator -----------" << endl;
|
||||
int value = *list1;
|
||||
cout << "Value at *list1: " << value << endl;
|
||||
cout << "----------- Test ++ operator -----------" << endl;
|
||||
list1.display();
|
||||
++list1;
|
||||
cout << "After ++list1: " << endl;
|
||||
list1.display();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
*
|
||||
* \file
|
||||
* \brief [Disjoint Sets Data Structure
|
||||
* (Disjoint Sets)](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
|
||||
*
|
||||
* \author [leoyang429](https://github.com/leoyang429)
|
||||
*
|
||||
* \details
|
||||
* A disjoint set data structure (also called union find or merge find set)
|
||||
* is a data structure that tracks a set of elements partitioned into a number
|
||||
* of disjoint (non-overlapping) subsets.
|
||||
* Some situations where disjoint sets can be used are-
|
||||
* to find connected components of a graph, kruskal's algorithm for finding
|
||||
* Minimum Spanning Tree etc.
|
||||
* There are two operation which we perform on disjoint sets -
|
||||
* 1) Union
|
||||
* 2) Find
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::vector;
|
||||
|
||||
vector<int> root, rank;
|
||||
|
||||
/**
|
||||
*
|
||||
* Function to create a set
|
||||
* @param n number of element
|
||||
*
|
||||
*/
|
||||
void CreateSet(int n) {
|
||||
root = vector<int>(n + 1);
|
||||
rank = vector<int>(n + 1, 1);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
root[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Find operation takes a number x and returns the set to which this number
|
||||
* belongs to.
|
||||
* @param x element of some set
|
||||
* @return set to which x belongs to
|
||||
*
|
||||
*/
|
||||
int Find(int x) {
|
||||
if (root[x] == x) {
|
||||
return x;
|
||||
}
|
||||
return root[x] = Find(root[x]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* A utility function to check if x and y are from same set or not
|
||||
* @param x element of some set
|
||||
* @param y element of some set
|
||||
*
|
||||
*/
|
||||
bool InSameUnion(int x, int y) { return Find(x) == Find(y); }
|
||||
|
||||
/**
|
||||
*
|
||||
* Union operation combines two disjoint sets to make a single set
|
||||
* in this union function we pass two elements and check if they are
|
||||
* from different sets then combine those sets
|
||||
* @param x element of some set
|
||||
* @param y element of some set
|
||||
*
|
||||
*/
|
||||
void Union(int x, int y) {
|
||||
int a = Find(x), b = Find(y);
|
||||
if (a != b) {
|
||||
if (rank[a] < rank[b]) {
|
||||
root[a] = b;
|
||||
} else if (rank[a] > rank[b]) {
|
||||
root[b] = a;
|
||||
} else {
|
||||
root[a] = b;
|
||||
++rank[b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Main function */
|
||||
int main() {
|
||||
// tests CreateSet & Find
|
||||
int n = 100;
|
||||
CreateSet(n);
|
||||
for (int i = 1; i <= 100; ++i) {
|
||||
if (root[i] != i) {
|
||||
cout << "Fail" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// tests InSameUnion & Union
|
||||
cout << "1 and 2 are initially not in the same subset" << endl;
|
||||
if (InSameUnion(1, 2)) {
|
||||
cout << "Fail" << endl;
|
||||
}
|
||||
Union(1, 2);
|
||||
cout << "1 and 2 are now in the same subset" << endl;
|
||||
if (!InSameUnion(1, 2)) {
|
||||
cout << "Fail" << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
struct node {
|
||||
int val;
|
||||
node *prev;
|
||||
node *next;
|
||||
} * start;
|
||||
|
||||
class double_linked_list {
|
||||
public:
|
||||
double_linked_list() { start = NULL; }
|
||||
void insert(int x);
|
||||
void remove(int x);
|
||||
void search(int x);
|
||||
void show();
|
||||
void reverseShow();
|
||||
};
|
||||
|
||||
void double_linked_list::insert(int x) {
|
||||
node *t = start;
|
||||
if (start != NULL) {
|
||||
while (t->next != NULL) {
|
||||
t = t->next;
|
||||
}
|
||||
node *n = new node;
|
||||
t->next = n;
|
||||
n->prev = t;
|
||||
n->val = x;
|
||||
n->next = NULL;
|
||||
} else {
|
||||
node *n = new node;
|
||||
n->val = x;
|
||||
n->prev = NULL;
|
||||
n->next = NULL;
|
||||
start = n;
|
||||
}
|
||||
}
|
||||
|
||||
void double_linked_list::remove(int x) {
|
||||
node *t = start;
|
||||
while (t != NULL && t->val != x) {
|
||||
t = t->next;
|
||||
}
|
||||
if (t == NULL) {
|
||||
return;
|
||||
}
|
||||
if (t->prev == NULL) {
|
||||
if (t->next == NULL) {
|
||||
start = NULL;
|
||||
} else {
|
||||
start = t->next;
|
||||
start->prev = NULL;
|
||||
}
|
||||
} else if (t->next == NULL) {
|
||||
t->prev->next = NULL;
|
||||
} else {
|
||||
t->prev->next = t->next;
|
||||
t->next->prev = t->prev;
|
||||
}
|
||||
delete t;
|
||||
}
|
||||
|
||||
void double_linked_list::search(int x) {
|
||||
node *t = start;
|
||||
int found = 0;
|
||||
while (t != NULL) {
|
||||
if (t->val == x) {
|
||||
std::cout << "\nFound";
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
t = t->next;
|
||||
}
|
||||
if (found == 0) {
|
||||
std::cout << "\nNot Found";
|
||||
}
|
||||
}
|
||||
|
||||
void double_linked_list::show() {
|
||||
node *t = start;
|
||||
while (t != NULL) {
|
||||
std::cout << t->val << "\t";
|
||||
t = t->next;
|
||||
}
|
||||
}
|
||||
|
||||
void double_linked_list::reverseShow() {
|
||||
node *t = start;
|
||||
while (t != NULL && t->next != NULL) {
|
||||
t = t->next;
|
||||
}
|
||||
while (t != NULL) {
|
||||
std::cout << t->val << "\t";
|
||||
t = t->prev;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int choice, x;
|
||||
double_linked_list ob;
|
||||
do {
|
||||
std::cout << "\n1. Insert";
|
||||
std::cout << "\n2. Delete";
|
||||
std::cout << "\n3. Search";
|
||||
std::cout << "\n4. Forward print";
|
||||
std::cout << "\n5. Reverse print";
|
||||
std::cout << "\n\nEnter you choice : ";
|
||||
std::cin >> choice;
|
||||
switch (choice) {
|
||||
case 1:
|
||||
std::cout << "\nEnter the element to be inserted : ";
|
||||
std::cin >> x;
|
||||
ob.insert(x);
|
||||
break;
|
||||
case 2:
|
||||
std::cout << "\nEnter the element to be removed : ";
|
||||
std::cin >> x;
|
||||
ob.remove(x);
|
||||
break;
|
||||
case 3:
|
||||
std::cout << "\nEnter the element to be searched : ";
|
||||
std::cin >> x;
|
||||
ob.search(x);
|
||||
break;
|
||||
case 4:
|
||||
ob.show();
|
||||
break;
|
||||
case 5:
|
||||
ob.reverseShow();
|
||||
break;
|
||||
}
|
||||
} while (choice != 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [DSU (Disjoint
|
||||
* sets)](https://en.wikipedia.org/wiki/Disjoint-set-data_structure)
|
||||
* @details
|
||||
* It is a very powerful data structure that keeps track of different
|
||||
* clusters(sets) of elements, these sets are disjoint(doesnot have a common
|
||||
* element). Disjoint sets uses cases : for finding connected components in a
|
||||
* graph, used in Kruskal's algorithm for finding Minimum Spanning tree.
|
||||
* Operations that can be performed:
|
||||
* 1) UnionSet(i,j): add(element i and j to the set)
|
||||
* 2) findSet(i): returns the representative of the set to which i belogngs to.
|
||||
* 3) get_max(i),get_min(i) : returns the maximum and minimum
|
||||
* Below is the class-based approach which uses the heuristic of path
|
||||
* compression. Using path compression in findSet(i),we are able to get to the
|
||||
* representative of i in O(1) time.
|
||||
* @author [AayushVyasKIIT](https://github.com/AayushVyasKIIT)
|
||||
* @see dsu_union_rank.cpp
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* @brief Disjoint sets union data structure, class based representation.
|
||||
* @param n number of elements
|
||||
*/
|
||||
class dsu {
|
||||
private:
|
||||
vector<uint64_t> p; ///< keeps track of the parent of ith element
|
||||
vector<uint64_t> depth; ///< tracks the depth(rank) of i in the tree
|
||||
vector<uint64_t> setSize; ///< size of each chunk(set)
|
||||
vector<uint64_t> maxElement; ///< maximum of each set to which i belongs to
|
||||
vector<uint64_t> minElement; ///< minimum of each set to which i belongs to
|
||||
public:
|
||||
/**
|
||||
* @brief contructor for initialising all data members.
|
||||
* @param n number of elements
|
||||
*/
|
||||
explicit dsu(uint64_t n) {
|
||||
p.assign(n, 0);
|
||||
/// initially, all of them are their own parents
|
||||
for (uint64_t i = 0; i < n; i++) {
|
||||
p[i] = i;
|
||||
}
|
||||
/// initially all have depth are equals to zero
|
||||
depth.assign(n, 0);
|
||||
maxElement.assign(n, 0);
|
||||
minElement.assign(n, 0);
|
||||
for (uint64_t i = 0; i < n; i++) {
|
||||
depth[i] = 0;
|
||||
maxElement[i] = i;
|
||||
minElement[i] = i;
|
||||
}
|
||||
setSize.assign(n, 0);
|
||||
/// initially set size will be equals to one
|
||||
for (uint64_t i = 0; i < n; i++) {
|
||||
setSize[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Method to find the representative of the set to which i belongs
|
||||
* to, T(n) = O(1)
|
||||
* @param i element of some set
|
||||
* @returns representative of the set to which i belongs to.
|
||||
*/
|
||||
uint64_t findSet(uint64_t i) {
|
||||
/// using path compression
|
||||
if (p[i] == i) {
|
||||
return i;
|
||||
}
|
||||
return (p[i] = findSet(p[i]));
|
||||
}
|
||||
/**
|
||||
* @brief Method that combines two disjoint sets to which i and j belongs to
|
||||
* and make a single set having a common representative.
|
||||
* @param i element of some set
|
||||
* @param j element of some set
|
||||
* @returns void
|
||||
*/
|
||||
void UnionSet(uint64_t i, uint64_t j) {
|
||||
/// check if both belongs to the same set or not
|
||||
if (isSame(i, j)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we find the representative of the i and j
|
||||
uint64_t x = findSet(i);
|
||||
uint64_t y = findSet(j);
|
||||
|
||||
/// always keeping the min as x
|
||||
/// shallow tree
|
||||
if (depth[x] > depth[y]) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
/// making the shallower root's parent the deeper root
|
||||
p[x] = y;
|
||||
|
||||
/// if same depth, then increase one's depth
|
||||
if (depth[x] == depth[y]) {
|
||||
depth[y]++;
|
||||
}
|
||||
/// total size of the resultant set
|
||||
setSize[y] += setSize[x];
|
||||
/// changing the maximum elements
|
||||
maxElement[y] = std::max(maxElement[x], maxElement[y]);
|
||||
minElement[y] = std::min(minElement[x], minElement[y]);
|
||||
}
|
||||
/**
|
||||
* @brief A utility function which check whether i and j belongs to
|
||||
* same set or not
|
||||
* @param i element of some set
|
||||
* @param j element of some set
|
||||
* @returns `true` if element `i` and `j` ARE in the same set
|
||||
* @returns `false` if element `i` and `j` are NOT in same set
|
||||
*/
|
||||
bool isSame(uint64_t i, uint64_t j) {
|
||||
if (findSet(i) == findSet(j)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @brief prints the minimum, maximum and size of the set to which i belongs
|
||||
* to
|
||||
* @param i element of some set
|
||||
* @returns void
|
||||
*/
|
||||
vector<uint64_t> get(uint64_t i) {
|
||||
vector<uint64_t> ans;
|
||||
ans.push_back(get_min(i));
|
||||
ans.push_back(get_max(i));
|
||||
ans.push_back(size(i));
|
||||
return ans;
|
||||
}
|
||||
/**
|
||||
* @brief A utility function that returns the size of the set to which i
|
||||
* belongs to
|
||||
* @param i element of some set
|
||||
* @returns size of the set to which i belongs to
|
||||
*/
|
||||
uint64_t size(uint64_t i) { return setSize[findSet(i)]; }
|
||||
/**
|
||||
* @brief A utility function that returns the max element of the set to
|
||||
* which i belongs to
|
||||
* @param i element of some set
|
||||
* @returns maximum of the set to which i belongs to
|
||||
*/
|
||||
uint64_t get_max(uint64_t i) { return maxElement[findSet(i)]; }
|
||||
/**
|
||||
* @brief A utility function that returns the min element of the set to
|
||||
* which i belongs to
|
||||
* @param i element of some set
|
||||
* @returns minimum of the set to which i belongs to
|
||||
*/
|
||||
uint64_t get_min(uint64_t i) { return minElement[findSet(i)]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations, 1st test
|
||||
* @returns void
|
||||
*/
|
||||
static void test1() {
|
||||
// the minimum, maximum, and size of the set
|
||||
uint64_t n = 10; ///< number of items
|
||||
dsu d(n + 1); ///< object of class disjoint sets
|
||||
// set 1
|
||||
d.UnionSet(1, 2); // performs union operation on 1 and 2
|
||||
d.UnionSet(1, 4); // performs union operation on 1 and 4
|
||||
vector<uint64_t> ans = {1, 4, 3};
|
||||
for (uint64_t i = 0; i < ans.size(); i++) {
|
||||
assert(d.get(4).at(i) == ans[i]); // makes sure algorithm works fine
|
||||
}
|
||||
cout << "1st test passed!" << endl;
|
||||
}
|
||||
/**
|
||||
* @brief Self-implementations, 2nd test
|
||||
* @returns void
|
||||
*/
|
||||
static void test2() {
|
||||
// the minimum, maximum, and size of the set
|
||||
uint64_t n = 10; ///< number of items
|
||||
dsu d(n + 1); ///< object of class disjoint sets
|
||||
// set 1
|
||||
d.UnionSet(3, 5);
|
||||
d.UnionSet(5, 6);
|
||||
d.UnionSet(5, 7);
|
||||
vector<uint64_t> ans = {3, 7, 4};
|
||||
for (uint64_t i = 0; i < ans.size(); i++) {
|
||||
assert(d.get(3).at(i) == ans[i]); // makes sure algorithm works fine
|
||||
}
|
||||
cout << "2nd test passed!" << endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
* */
|
||||
int main() {
|
||||
uint64_t n = 10; ///< number of items
|
||||
dsu d(n + 1); ///< object of class disjoint sets
|
||||
|
||||
test1(); // run 1st test case
|
||||
test2(); // run 2nd test case
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [DSU (Disjoint
|
||||
* sets)](https://en.wikipedia.org/wiki/Disjoint-set-data_structure)
|
||||
* @details
|
||||
* dsu : It is a very powerful data structure which keeps track of different
|
||||
* clusters(sets) of elements, these sets are disjoint(doesnot have a common
|
||||
* element). Disjoint sets uses cases : for finding connected components in a
|
||||
* graph, used in Kruskal's algorithm for finding Minimum Spanning tree.
|
||||
* Operations that can be performed:
|
||||
* 1) UnionSet(i,j): add(element i and j to the set)
|
||||
* 2) findSet(i): returns the representative of the set to which i belogngs to.
|
||||
* 3) getParents(i): prints the parent of i and so on and so forth.
|
||||
* Below is the class-based approach which uses the heuristic of union-ranks.
|
||||
* Using union-rank in findSet(i),we are able to get to the representative of i
|
||||
* in slightly delayed O(logN) time but it allows us to keep tracks of the
|
||||
* parent of i.
|
||||
* @author [AayushVyasKIIT](https://github.com/AayushVyasKIIT)
|
||||
* @see dsu_path_compression.cpp
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* @brief Disjoint sets union data structure, class based representation.
|
||||
* @param n number of elements
|
||||
*/
|
||||
class dsu {
|
||||
private:
|
||||
vector<uint64_t> p; ///< keeps track of the parent of ith element
|
||||
vector<uint64_t> depth; ///< tracks the depth(rank) of i in the tree
|
||||
vector<uint64_t> setSize; ///< size of each chunk(set)
|
||||
public:
|
||||
/**
|
||||
* @brief constructor for initialising all data members
|
||||
* @param n number of elements
|
||||
*/
|
||||
explicit dsu(uint64_t n) {
|
||||
p.assign(n, 0);
|
||||
/// initially all of them are their own parents
|
||||
depth.assign(n, 0);
|
||||
setSize.assign(n, 0);
|
||||
for (uint64_t i = 0; i < n; i++) {
|
||||
p[i] = i;
|
||||
depth[i] = 0;
|
||||
setSize[i] = 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Method to find the representative of the set to which i belongs
|
||||
* to, T(n) = O(logN)
|
||||
* @param i element of some set
|
||||
* @returns representative of the set to which i belongs to
|
||||
*/
|
||||
uint64_t findSet(uint64_t i) {
|
||||
/// using union-rank
|
||||
while (i != p[i]) {
|
||||
i = p[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/**
|
||||
* @brief Method that combines two disjoint sets to which i and j belongs to
|
||||
* and make a single set having a common representative.
|
||||
* @param i element of some set
|
||||
* @param j element of some set
|
||||
* @returns void
|
||||
*/
|
||||
void unionSet(uint64_t i, uint64_t j) {
|
||||
/// checks if both belongs to same set or not
|
||||
if (isSame(i, j)) {
|
||||
return;
|
||||
}
|
||||
/// we find representative of the i and j
|
||||
uint64_t x = findSet(i);
|
||||
uint64_t y = findSet(j);
|
||||
|
||||
/// always keeping the min as x
|
||||
/// in order to create a shallow tree
|
||||
if (depth[x] > depth[y]) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
/// making the shallower tree, root parent of the deeper root
|
||||
p[x] = y;
|
||||
|
||||
/// if same depth, then increase one's depth
|
||||
if (depth[x] == depth[y]) {
|
||||
depth[y]++;
|
||||
}
|
||||
/// total size of the resultant set
|
||||
setSize[y] += setSize[x];
|
||||
}
|
||||
/**
|
||||
* @brief A utility function which check whether i and j belongs to same set
|
||||
* or not
|
||||
* @param i element of some set
|
||||
* @param j element of some set
|
||||
* @returns `true` if element i and j are in same set
|
||||
* @returns `false` if element i and j are not in same set
|
||||
*/
|
||||
bool isSame(uint64_t i, uint64_t j) {
|
||||
if (findSet(i) == findSet(j)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @brief Method to print all the parents of i, or the path from i to
|
||||
* representative.
|
||||
* @param i element of some set
|
||||
* @returns void
|
||||
*/
|
||||
vector<uint64_t> getParents(uint64_t i) {
|
||||
vector<uint64_t> ans;
|
||||
while (p[i] != i) {
|
||||
ans.push_back(i);
|
||||
i = p[i];
|
||||
}
|
||||
ans.push_back(i);
|
||||
return ans;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @brief Self-implementations, 1st test
|
||||
* @returns void
|
||||
*/
|
||||
static void test1() {
|
||||
/* checks the parents in the resultant structures */
|
||||
uint64_t n = 10; ///< number of elements
|
||||
dsu d(n + 1); ///< object of class disjoint sets
|
||||
d.unionSet(2, 1); ///< performs union operation on 1 and 2
|
||||
d.unionSet(1, 4);
|
||||
d.unionSet(8, 1);
|
||||
d.unionSet(3, 5);
|
||||
d.unionSet(5, 6);
|
||||
d.unionSet(5, 7);
|
||||
d.unionSet(9, 10);
|
||||
d.unionSet(2, 10);
|
||||
// keeping track of the changes using parent pointers
|
||||
vector<uint64_t> ans = {7, 5};
|
||||
for (uint64_t i = 0; i < ans.size(); i++) {
|
||||
assert(d.getParents(7).at(i) ==
|
||||
ans[i]); // makes sure algorithm works fine
|
||||
}
|
||||
cout << "1st test passed!" << endl;
|
||||
}
|
||||
/**
|
||||
* @brief Self-implementations, 2nd test
|
||||
* @returns void
|
||||
*/
|
||||
static void test2() {
|
||||
// checks the parents in the resultant structures
|
||||
uint64_t n = 10; ///< number of elements
|
||||
dsu d(n + 1); ///< object of class disjoint sets
|
||||
d.unionSet(2, 1); /// performs union operation on 1 and 2
|
||||
d.unionSet(1, 4);
|
||||
d.unionSet(8, 1);
|
||||
d.unionSet(3, 5);
|
||||
d.unionSet(5, 6);
|
||||
d.unionSet(5, 7);
|
||||
d.unionSet(9, 10);
|
||||
d.unionSet(2, 10);
|
||||
|
||||
/// keeping track of the changes using parent pointers
|
||||
vector<uint64_t> ans = {2, 1, 10};
|
||||
for (uint64_t i = 0; i < ans.size(); i++) {
|
||||
assert(d.getParents(2).at(i) ==
|
||||
ans[i]); /// makes sure algorithm works fine
|
||||
}
|
||||
cout << "2nd test passed!" << endl;
|
||||
}
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test1(); // run 1st test case
|
||||
test2(); // run 2nd test case
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Implementation of singly linked list algorithm.
|
||||
* @details
|
||||
* The linked list is a data structure used for holding a sequence of
|
||||
* values, which can be added, removed and displayed.
|
||||
* ### Algorithm
|
||||
* Values can be added by iterating to the end of a list(by following
|
||||
* the pointers) starting from the first link. Whichever link points to null
|
||||
* is considered the last link and is pointed to the new value.
|
||||
*
|
||||
* Values can be removed by also iterating through the list. When the node
|
||||
* containing the value is found, the node pointing to the current node is made
|
||||
* to point to the node that the current node is pointing to, and then returning
|
||||
* the current node to heap store.
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data Structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
|
||||
/**
|
||||
* @namespace linked_list
|
||||
* @brief Functions for singly linked list algorithm
|
||||
*/
|
||||
namespace linked_list {
|
||||
|
||||
/**
|
||||
* This function checks if the string passed consists
|
||||
* of only digits.
|
||||
* @param s To be checked if s contains only integers
|
||||
* @returns true if there are only digits present in the string
|
||||
* @returns false if any other character is found
|
||||
*/
|
||||
bool isDigit(const std::string& s) {
|
||||
// function statements here
|
||||
for (char i : s) {
|
||||
if (!isdigit(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A link class containing a value and pointer to another link
|
||||
*/
|
||||
class link {
|
||||
private:
|
||||
int pvalue; ///< value of the current link
|
||||
std::shared_ptr<link> psucc; ///< pointer to the next value on the list
|
||||
|
||||
public:
|
||||
/**
|
||||
* function returns the integer value stored in the link.
|
||||
* @returns the integer value stored in the link.
|
||||
*/
|
||||
int val() { return pvalue; }
|
||||
|
||||
/**
|
||||
* function returns the pointer to next link
|
||||
* @returns the pointer to the next link
|
||||
* */
|
||||
std::shared_ptr<link>& succ() { return psucc; }
|
||||
|
||||
/**
|
||||
* Creates link with provided value and pointer to next link
|
||||
* @param value is the integer stored in the link
|
||||
*/
|
||||
explicit link(int value = 0) : pvalue(value), psucc(nullptr) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A list class containing a sequence of links
|
||||
*/
|
||||
class list {
|
||||
private:
|
||||
std::shared_ptr<link> first; ///< link before the actual first element
|
||||
std::shared_ptr<link> last; ///< last link on the list
|
||||
public:
|
||||
/**
|
||||
* List constructor. Initializes the first and last link.
|
||||
*/
|
||||
list() {
|
||||
// Initialize the first link
|
||||
first = std::make_shared<link>();
|
||||
// Initialize the last link with the first link
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
bool isEmpty();
|
||||
|
||||
void push_back(int new_elem);
|
||||
void push_front(int new_elem);
|
||||
void erase(int old_elem);
|
||||
void display();
|
||||
std::shared_ptr<link> search(int find_elem);
|
||||
void reverse();
|
||||
};
|
||||
|
||||
/**
|
||||
* function checks if list is empty
|
||||
* @returns true if list is empty
|
||||
* @returns false if list is not empty
|
||||
*/
|
||||
bool list::isEmpty() {
|
||||
if (last == nullptr) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function adds new element to the end of the list
|
||||
* @param new_elem to be added to the end of the list
|
||||
*/
|
||||
void list::push_back(int new_elem) {
|
||||
if (isEmpty()) {
|
||||
first->succ() = std::make_shared<link>(new_elem);
|
||||
last = first->succ();
|
||||
} else {
|
||||
last->succ() = std::make_shared<link>(new_elem);
|
||||
last = last->succ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function adds new element to the beginning of the list
|
||||
* @param new_elem to be added to front of the list
|
||||
*/
|
||||
void list::push_front(int new_elem) {
|
||||
if (isEmpty()) {
|
||||
first->succ() = std::make_shared<link>(new_elem);
|
||||
last = first->succ();
|
||||
} else {
|
||||
std::shared_ptr<link> t = std::make_shared<link>(new_elem);
|
||||
t->succ() = first->succ();
|
||||
first->succ() = t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function erases old element from the list
|
||||
* @param old_elem to be erased from the list
|
||||
*/
|
||||
void list::erase(int old_elem) {
|
||||
if (isEmpty()) {
|
||||
std::cout << "List is Empty!";
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<link> t = first;
|
||||
std::shared_ptr<link> to_be_removed = nullptr;
|
||||
while (t != last && t->succ()->val() != old_elem) {
|
||||
t = t->succ();
|
||||
}
|
||||
if (t == last) {
|
||||
std::cout << "Element not found\n";
|
||||
return;
|
||||
}
|
||||
to_be_removed = t->succ();
|
||||
t->succ() = t->succ()->succ();
|
||||
to_be_removed.reset();
|
||||
if (t->succ() == nullptr) {
|
||||
last = t;
|
||||
}
|
||||
if (first == last){
|
||||
last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function displays all the elements in the list
|
||||
* @returns 'void'
|
||||
*/
|
||||
void list::display() {
|
||||
if (isEmpty()) {
|
||||
std::cout << "List is Empty!";
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<link> t = first;
|
||||
while (t->succ() != nullptr) {
|
||||
std::cout << t->succ()->val() << "\t";
|
||||
t = t->succ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function searchs for @param find_elem in the list
|
||||
* @param find_elem to be searched for in the list
|
||||
*/
|
||||
std::shared_ptr<link> list::search(int find_elem) {
|
||||
if (isEmpty()) {
|
||||
std::cout << "List is Empty!";
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<link> t = first;
|
||||
while (t != last && t->succ()->val() != find_elem) {
|
||||
t = t->succ();
|
||||
}
|
||||
if (t == last) {
|
||||
std::cout << "Element not found\n";
|
||||
return nullptr;
|
||||
}
|
||||
std::cout << "Element was found\n";
|
||||
return t->succ();
|
||||
}
|
||||
} // namespace linked_list
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* Main function:
|
||||
* Allows the user add and delete values from the list.
|
||||
* Also allows user to search for and display values in the list.
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
data_structures::linked_list::list l;
|
||||
int choice = 0;
|
||||
int x = 0;
|
||||
std::string s;
|
||||
do {
|
||||
std::cout << "\n1. Insert";
|
||||
std::cout << "\n2. Delete";
|
||||
std::cout << "\n3. Search";
|
||||
std::cout << "\n4. Print";
|
||||
std::cout << "\n0. Exit";
|
||||
std::cout << "\n\nEnter you choice : ";
|
||||
std::cin >> choice;
|
||||
switch (choice) {
|
||||
case 0:
|
||||
std::cout << "\nQuitting the program...\n";
|
||||
break;
|
||||
case 1:
|
||||
std::cout << "\nEnter the element to be inserted : ";
|
||||
std::cin >> s;
|
||||
|
||||
if (data_structures::linked_list::isDigit(s)) {
|
||||
x = std::stoi(s);
|
||||
l.push_back(x);
|
||||
} else {
|
||||
std::cout << "Wrong Input!\n";
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
std::cout << "\nEnter the element to be removed : ";
|
||||
std::cin >> s;
|
||||
if (data_structures::linked_list::isDigit(s)) {
|
||||
x = std::stoi(s);
|
||||
l.erase(x);
|
||||
} else {
|
||||
std::cout << "Wrong Input!\n";
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
std::cout << "\nEnter the element to be searched : ";
|
||||
std::cin >> s;
|
||||
if (data_structures::linked_list::isDigit(s)) {
|
||||
x = std::stoi(s);
|
||||
std::shared_ptr<data_structures::linked_list::link> found =
|
||||
l.search(x);
|
||||
} else {
|
||||
std::cout << "Wrong Input!\n";
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
l.display();
|
||||
std::cout << "\n";
|
||||
break;
|
||||
default:
|
||||
std::cout << "Invalid Input\n" << std::endl;
|
||||
break;
|
||||
}
|
||||
} while (choice != 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Linked list implementation using Arrays
|
||||
*
|
||||
* The difference between the pointer implementation of linked list and array
|
||||
* implementation of linked list:
|
||||
* 1. The NULL is represented by -1;
|
||||
* 2. Limited size. (in the following case it is 100 nodes at max). But we can
|
||||
* reuse the nodes that are to be deleted by again linking it bacj to the list.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
struct Node {
|
||||
int data;
|
||||
int next;
|
||||
};
|
||||
|
||||
Node AvailArray[100]; ///< array that will act as nodes of a linked list.
|
||||
|
||||
int head = -1;
|
||||
int avail = 0;
|
||||
void initialise_list() {
|
||||
for (int i = 0; i <= 98; i++) {
|
||||
AvailArray[i].next = i + 1;
|
||||
}
|
||||
AvailArray[99].next = -1; // indicating the end of the linked list.
|
||||
}
|
||||
|
||||
/** This will return the index of the first free node present in the avail list
|
||||
*/
|
||||
int getnode() {
|
||||
int NodeIndexToBeReturned = avail;
|
||||
avail = AvailArray[avail].next;
|
||||
return NodeIndexToBeReturned;
|
||||
}
|
||||
|
||||
/** This function when called will delete the node with
|
||||
* the index presented as an argument, and will put
|
||||
* back that node into the array.
|
||||
*/
|
||||
void freeNode(int nodeToBeDeleted) {
|
||||
AvailArray[nodeToBeDeleted].next = avail;
|
||||
avail = nodeToBeDeleted;
|
||||
}
|
||||
|
||||
/** The function will insert the given data
|
||||
* into the front of the linked list.
|
||||
*/
|
||||
void insertAtTheBeginning(int data) {
|
||||
int newNode = getnode();
|
||||
AvailArray[newNode].data = data;
|
||||
AvailArray[newNode].next = head;
|
||||
head = newNode;
|
||||
}
|
||||
|
||||
void insertAtTheEnd(int data) {
|
||||
int newNode = getnode();
|
||||
int temp = head;
|
||||
while (AvailArray[temp].next != -1) {
|
||||
temp = AvailArray[temp].next;
|
||||
}
|
||||
// temp is now pointing to the end node.
|
||||
AvailArray[newNode].data = data;
|
||||
AvailArray[newNode].next = -1;
|
||||
AvailArray[temp].next = newNode;
|
||||
}
|
||||
|
||||
void display() {
|
||||
int temp = head;
|
||||
while (temp != -1) {
|
||||
std::cout << AvailArray[temp].data << "->";
|
||||
temp = AvailArray[temp].next;
|
||||
}
|
||||
std::cout << "-1" << std::endl;
|
||||
}
|
||||
|
||||
/** Main function */
|
||||
int main() {
|
||||
initialise_list();
|
||||
int x, y, z;
|
||||
for (;;) {
|
||||
std::cout << "1. Insert At The Beginning" << std::endl;
|
||||
std::cout << "2. Insert At The End" << std::endl;
|
||||
std::cout << "3. Display" << std::endl;
|
||||
std::cout << "4.Exit" << std::endl;
|
||||
std::cout << "Enter Your choice" << std::endl;
|
||||
std::cin >> z;
|
||||
switch (z) {
|
||||
case 1:
|
||||
std::cout << "Enter the number you want to enter" << std::endl;
|
||||
std::cin >> x;
|
||||
insertAtTheBeginning(x);
|
||||
break;
|
||||
case 2:
|
||||
std::cout << "Enter the number you want to enter" << std::endl;
|
||||
std::cin >> y;
|
||||
insertAtTheEnd(y);
|
||||
break;
|
||||
case 3:
|
||||
std::cout
|
||||
<< "The linked list contains the following element in order"
|
||||
<< std::endl;
|
||||
display();
|
||||
break;
|
||||
case 4:
|
||||
return 0;
|
||||
default:
|
||||
std::cout << "The entered choice is not correct" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @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 <array> /// for std::array
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for io operations
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template <uint64_t N>
|
||||
struct list {
|
||||
std::array<uint64_t, N> 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<uint64_t, N> &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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<uint64_t, N> &dataArr,
|
||||
const uint64_t &val) const {
|
||||
// Going through each element in the list
|
||||
for (uint64_t i = 0; i < top; i++) {
|
||||
if (dataArr[i] == val) {
|
||||
return i; // element found at ith index
|
||||
}
|
||||
}
|
||||
// element is not present in the list
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* @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 == N) {
|
||||
std::cout << "\nOverflow";
|
||||
return;
|
||||
}
|
||||
// if list is not sorted, insert at the last
|
||||
// otherwise place it to correct position
|
||||
if (!isSorted) {
|
||||
data[top] = val;
|
||||
top++;
|
||||
} else {
|
||||
uint64_t pos = 0; // Initialize the index variable
|
||||
// Going through each element and find correct position for element
|
||||
for (uint64_t i = 0; i < top - 1; i++) {
|
||||
// check for the correct position
|
||||
if (data[i] <= val && val <= data[i + 1]) {
|
||||
pos = i + 1; // assign correct pos to the index var
|
||||
break; // to get out from the loop
|
||||
}
|
||||
}
|
||||
// if all elements are smaller than the element
|
||||
if (pos == 0) {
|
||||
pos = top - 1;
|
||||
}
|
||||
// shift all element to make a room for new element
|
||||
for (uint64_t i = top; i > pos; i--) {
|
||||
data[i] = data[i - 1];
|
||||
}
|
||||
top++; // Increment the value of top.
|
||||
data[pos] =
|
||||
val; // Assign the value to the correct index in the array
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To remove the element from the list
|
||||
* @param val element that will be removed
|
||||
* @returns void
|
||||
*/
|
||||
void remove(const uint64_t &val) {
|
||||
uint64_t pos = search(val); // search the index of the value
|
||||
// if search returns -1, element does not present in the list
|
||||
if (pos == -1) {
|
||||
std::cout << "\n Element does not present in the list ";
|
||||
return;
|
||||
}
|
||||
std::cout << "\n"
|
||||
<< data[pos] << " deleted"; // print the appropriate message
|
||||
// shift all the element 1 left to fill vacant space
|
||||
for (uint64_t i = pos; i < top; i++) {
|
||||
data[i] = data[i + 1];
|
||||
}
|
||||
top--; // decrement the top variable to maintain last index
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to print array
|
||||
* @returns void
|
||||
*/
|
||||
void show() {
|
||||
// Going through each element in the list
|
||||
std::cout << '\n';
|
||||
for (uint64_t i = 0; i < top; i++) {
|
||||
std::cout << data[i] << " "; // print the element
|
||||
}
|
||||
}
|
||||
}; // structure list
|
||||
} // namespace list_array
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::list_array::list<50> 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() {
|
||||
test(); // Execute the tests
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
/**************************
|
||||
@author shrutisheoran
|
||||
**************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Btree {
|
||||
int data;
|
||||
struct Btree *left; // Pointer to left subtree
|
||||
struct Btree *right; // Pointer to right subtree
|
||||
};
|
||||
|
||||
void insert(Btree **root, int d) {
|
||||
Btree *nn = new Btree(); // Creating new node
|
||||
nn->data = d;
|
||||
nn->left = NULL;
|
||||
nn->right = NULL;
|
||||
if (*root == NULL) {
|
||||
*root = nn;
|
||||
return;
|
||||
} else {
|
||||
queue<Btree *> q;
|
||||
// Adding root node to queue
|
||||
q.push(*root);
|
||||
while (!q.empty()) {
|
||||
Btree *node = q.front();
|
||||
// Removing parent node from queue
|
||||
q.pop();
|
||||
if (node->left)
|
||||
// Adding left child of removed node to queue
|
||||
q.push(node->left);
|
||||
else {
|
||||
// Adding new node if no left child is present
|
||||
node->left = nn;
|
||||
return;
|
||||
}
|
||||
if (node->right)
|
||||
// Adding right child of removed node to queue
|
||||
q.push(node->right);
|
||||
else {
|
||||
// Adding new node if no right child is present
|
||||
node->right = nn;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void morrisInorder(Btree *root) {
|
||||
Btree *curr = root;
|
||||
Btree *temp;
|
||||
while (curr) {
|
||||
if (curr->left == NULL) {
|
||||
cout << curr->data << " ";
|
||||
// If left of current node is NULL then curr is shifted to right
|
||||
curr = curr->right;
|
||||
} else {
|
||||
// Left of current node is stored in temp
|
||||
temp = curr->left;
|
||||
// Moving to extreme right of temp
|
||||
while (temp->right && temp->right != curr) temp = temp->right;
|
||||
// If extreme right is null it is made to point to currrent node
|
||||
// (will be used for backtracking)
|
||||
if (temp->right == NULL) {
|
||||
temp->right = curr;
|
||||
// current node is made to point its left subtree
|
||||
curr = curr->left;
|
||||
}
|
||||
// If extreme right already points to currrent node it it set to
|
||||
// null
|
||||
else if (temp->right == curr) {
|
||||
cout << curr->data << " ";
|
||||
temp->right = NULL;
|
||||
// current node is made to point its right subtree
|
||||
curr = curr->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAll(const Btree *const root) {
|
||||
if (root) {
|
||||
deleteAll(root->left);
|
||||
deleteAll(root->right);
|
||||
delete root;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Testing morrisInorder funtion
|
||||
Btree *root = NULL;
|
||||
int i;
|
||||
for (i = 1; i <= 7; i++) insert(&root, i);
|
||||
cout << "Morris Inorder: ";
|
||||
morrisInorder(root);
|
||||
deleteAll(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Provides Node class and related utilities
|
||||
**/
|
||||
#ifndef DATA_STRUCTURES_NODE_HPP_
|
||||
#define DATA_STRUCTURES_NODE_HPP_
|
||||
|
||||
#include <iostream> /// for std::cout
|
||||
#include <memory> /// for std::shared_ptr
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/** Definition of the node as a linked-list
|
||||
* \tparam ValueType type of data nodes of the linked list should contain
|
||||
*/
|
||||
template <class ValueType>
|
||||
struct Node {
|
||||
using value_type = ValueType;
|
||||
ValueType data = {};
|
||||
std::shared_ptr<Node<ValueType>> next = {};
|
||||
};
|
||||
|
||||
template <typename Node, typename Action>
|
||||
void traverse(const Node* const inNode, const Action& action) {
|
||||
if (inNode) {
|
||||
action(*inNode);
|
||||
traverse(inNode->next.get(), action);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void display_all(const Node* const inNode) {
|
||||
traverse(inNode,
|
||||
[](const Node& curNode) { std::cout << curNode.data << " "; });
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
std::vector<typename Node::value_type> push_all_to_vector(
|
||||
const Node* const inNode, const std::size_t expected_size = 0) {
|
||||
std::vector<typename Node::value_type> res;
|
||||
res.reserve(expected_size);
|
||||
traverse(inNode,
|
||||
[&res](const Node& curNode) { res.push_back(curNode.data); });
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // DATA_STRUCTURES_NODE_HPP_
|
||||
@@ -0,0 +1,104 @@
|
||||
/* This class specifies the basic operation on a queue as a linked list */
|
||||
#ifndef DATA_STRUCTURES_QUEUE_HPP_
|
||||
#define DATA_STRUCTURES_QUEUE_HPP_
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
/** Definition of the queue class */
|
||||
template <class ValueType>
|
||||
class queue {
|
||||
using node_type = Node<ValueType>;
|
||||
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
/**
|
||||
* @brief prints the queue into the std::cout
|
||||
*/
|
||||
void display() const {
|
||||
std::cout << "Front --> ";
|
||||
display_all(this->queueFront.get());
|
||||
std::cout << '\n';
|
||||
std::cout << "Size of queue: " << size << '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief converts the queue into the std::vector
|
||||
* @return std::vector containning all of the elements of the queue in the
|
||||
* same order
|
||||
*/
|
||||
std::vector<value_type> toVector() const {
|
||||
return push_all_to_vector(this->queueFront.get(), this->size);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief throws an exception if queue is empty
|
||||
* @exception std::invalid_argument if queue is empty
|
||||
*/
|
||||
void ensureNotEmpty() const {
|
||||
if (isEmptyQueue()) {
|
||||
throw std::invalid_argument("Queue is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief checks if the queue has no elements
|
||||
* @return true if the queue is empty, false otherwise
|
||||
*/
|
||||
bool isEmptyQueue() const { return (queueFront == nullptr); }
|
||||
|
||||
/**
|
||||
* @brief inserts a new item into the queue
|
||||
*/
|
||||
void enQueue(const value_type& item) {
|
||||
auto newNode = std::make_shared<node_type>();
|
||||
newNode->data = item;
|
||||
newNode->next = nullptr;
|
||||
if (isEmptyQueue()) {
|
||||
queueFront = newNode;
|
||||
queueRear = newNode;
|
||||
} else {
|
||||
queueRear->next = newNode;
|
||||
queueRear = queueRear->next;
|
||||
}
|
||||
++size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first element of the queue
|
||||
* @exception std::invalid_argument if queue is empty
|
||||
*/
|
||||
value_type front() const {
|
||||
ensureNotEmpty();
|
||||
return queueFront->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes the first element from the queue
|
||||
* @exception std::invalid_argument if queue is empty
|
||||
*/
|
||||
void deQueue() {
|
||||
ensureNotEmpty();
|
||||
queueFront = queueFront->next;
|
||||
--size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes all elements from the queue
|
||||
*/
|
||||
void clear() {
|
||||
queueFront = nullptr;
|
||||
queueRear = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<node_type> queueFront =
|
||||
{}; /**< Pointer to the front of the queue */
|
||||
std::shared_ptr<node_type> queueRear =
|
||||
{}; /**< Pointer to the rear of the queue */
|
||||
std::size_t size = 0;
|
||||
};
|
||||
|
||||
#endif // DATA_STRUCTURES_QUEUE_HPP_
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Implementation of Linear [Queue using array]
|
||||
* (https://www.geeksforgeeks.org/array-implementation-of-queue-simple/).
|
||||
* @details
|
||||
* The Linear Queue is a data structure used for holding a sequence of
|
||||
* values, which can be added to the end line (enqueue), removed from
|
||||
* head of line (dequeue) and displayed.
|
||||
* ### Algorithm
|
||||
* Values can be added by increasing the `rear` variable by 1 (which points to
|
||||
* the end of the array), then assigning new value to `rear`'s element of the
|
||||
* array.
|
||||
*
|
||||
* Values can be removed by increasing the `front` variable by 1 (which points
|
||||
* to the first of the array), so it cannot reached any more.
|
||||
*
|
||||
* @author [Pooja](https://github.com/pooja-git11)
|
||||
* @author [Farbod Ahmadian](https://github.com/farbodahm)
|
||||
*/
|
||||
#include <array> /// for std::array
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for io operations
|
||||
|
||||
constexpr uint16_t max_size{10}; ///< Maximum size of the queue
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Algorithms with data structures
|
||||
*/
|
||||
namespace data_structures {
|
||||
|
||||
/**
|
||||
* @namespace queue_using_array
|
||||
* @brief Functions for [Queue using Array]
|
||||
* (https://www.geeksforgeeks.org/array-implementation-of-queue-simple/)
|
||||
* implementation.
|
||||
*/
|
||||
namespace queue_using_array {
|
||||
|
||||
/**
|
||||
* @brief Queue_Array class containing the main data and also index of head and
|
||||
* tail of the array.
|
||||
*/
|
||||
class Queue_Array {
|
||||
public:
|
||||
void enqueue(const int16_t&); ///< Add element to the first of the queue
|
||||
int dequeue(); ///< Delete element from back of the queue
|
||||
void display() const; ///< Show all saved data
|
||||
private:
|
||||
int8_t front{-1}; ///< Index of head of the array
|
||||
int8_t rear{-1}; ///< Index of tail of the array
|
||||
std::array<int16_t, max_size> arr{}; ///< All stored data
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adds new element to the end of the queue
|
||||
* @param ele to be added to the end of the queue
|
||||
*/
|
||||
void Queue_Array::enqueue(const int16_t& ele) {
|
||||
if (rear == arr.size() - 1) {
|
||||
std::cout << "\nStack is full";
|
||||
} else if (front == -1 && rear == -1) {
|
||||
front = 0;
|
||||
rear = 0;
|
||||
arr[rear] = ele;
|
||||
} else if (rear < arr.size()) {
|
||||
++rear;
|
||||
arr[rear] = ele;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove element that is located at the first of the queue
|
||||
* @returns data that is deleted if queue is not empty
|
||||
*/
|
||||
int Queue_Array::dequeue() {
|
||||
int8_t d{0};
|
||||
if (front == -1) {
|
||||
std::cout << "\nstack is empty ";
|
||||
return 0;
|
||||
} else if (front == rear) {
|
||||
d = arr.at(front);
|
||||
front = rear = -1;
|
||||
} else {
|
||||
d = arr.at(front++);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to show all elements in the queue
|
||||
*/
|
||||
void Queue_Array::display() const {
|
||||
if (front == -1) {
|
||||
std::cout << "\nStack is empty";
|
||||
} else {
|
||||
for (int16_t i{front}; i <= rear; ++i) std::cout << arr.at(i) << " ";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace queue_using_array
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @details
|
||||
* Allows the user to add and delete values from the queue.
|
||||
* Also allows user to display values in the queue.
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
int op{0}, data{0};
|
||||
data_structures::queue_using_array::Queue_Array ob;
|
||||
|
||||
std::cout << "\n1. enqueue(Insertion) ";
|
||||
std::cout << "\n2. dequeue(Deletion)";
|
||||
std::cout << "\n3. Display";
|
||||
std::cout << "\n4. Exit";
|
||||
while (true) {
|
||||
std::cout << "\nEnter your choice ";
|
||||
std::cin >> op;
|
||||
if (op == 1) {
|
||||
std::cout << "Enter data ";
|
||||
std::cin >> data;
|
||||
ob.enqueue(data);
|
||||
} else if (op == 2) {
|
||||
data = ob.dequeue();
|
||||
std::cout << "\ndequeue element is:\t" << data;
|
||||
} else if (op == 3) {
|
||||
ob.display();
|
||||
} else if (op == 4) {
|
||||
exit(0);
|
||||
} else {
|
||||
std::cout << "\nWrong choice ";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <iostream>
|
||||
|
||||
int queue[10];
|
||||
int front = 0;
|
||||
int rear = 0;
|
||||
|
||||
void Enque(int x) {
|
||||
if (rear == 10) {
|
||||
std::cout << "\nOverflow";
|
||||
} else {
|
||||
queue[rear++] = x;
|
||||
}
|
||||
}
|
||||
|
||||
void Deque() {
|
||||
if (front == rear) {
|
||||
std::cout << "\nUnderflow";
|
||||
}
|
||||
|
||||
else {
|
||||
std::cout << "\n" << queue[front++] << " deleted";
|
||||
for (int i = front; i < rear; i++) {
|
||||
queue[i - front] = queue[i];
|
||||
}
|
||||
rear = rear - front;
|
||||
front = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void show() {
|
||||
for (int i = front; i < rear; i++) {
|
||||
std::cout << queue[i] << "\t";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int ch, x;
|
||||
do {
|
||||
std::cout << "\n1. Enque";
|
||||
std::cout << "\n2. Deque";
|
||||
std::cout << "\n3. Print";
|
||||
std::cout << "\nEnter Your Choice : ";
|
||||
std::cin >> ch;
|
||||
if (ch == 1) {
|
||||
std::cout << "\nInsert : ";
|
||||
std::cin >> x;
|
||||
Enque(x);
|
||||
} else if (ch == 2) {
|
||||
Deque();
|
||||
} else if (ch == 3) {
|
||||
show();
|
||||
}
|
||||
} while (ch != 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
struct node {
|
||||
int val;
|
||||
node *next;
|
||||
};
|
||||
|
||||
node *front, *rear;
|
||||
|
||||
void Enque(int x) {
|
||||
if (rear == NULL) {
|
||||
node *n = new node;
|
||||
n->val = x;
|
||||
n->next = NULL;
|
||||
rear = n;
|
||||
front = n;
|
||||
}
|
||||
|
||||
else {
|
||||
node *n = new node;
|
||||
n->val = x;
|
||||
n->next = NULL;
|
||||
rear->next = n;
|
||||
rear = n;
|
||||
}
|
||||
}
|
||||
|
||||
void Deque() {
|
||||
if (rear == NULL && front == NULL) {
|
||||
cout << "\nUnderflow";
|
||||
} else {
|
||||
node *t = front;
|
||||
cout << "\n" << t->val << " deleted";
|
||||
front = front->next;
|
||||
delete t;
|
||||
if (front == NULL)
|
||||
rear = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void show() {
|
||||
node *t = front;
|
||||
while (t != NULL) {
|
||||
cout << t->val << "\t";
|
||||
t = t->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int ch, x;
|
||||
do {
|
||||
cout << "\n1. Enque";
|
||||
cout << "\n2. Deque";
|
||||
cout << "\n3. Print";
|
||||
cout << "\nEnter Your Choice : ";
|
||||
cin >> ch;
|
||||
if (ch == 1) {
|
||||
cout << "\nInsert : ";
|
||||
cin >> x;
|
||||
Enque(x);
|
||||
} else if (ch == 2) {
|
||||
Deque();
|
||||
} else if (ch == 3) {
|
||||
show();
|
||||
}
|
||||
} while (ch != 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Write a program to implement Queue using linkedlist.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
struct linkedlist {
|
||||
int data;
|
||||
linkedlist *next;
|
||||
};
|
||||
class stack_linkedList {
|
||||
public:
|
||||
linkedlist *front;
|
||||
linkedlist *rear;
|
||||
|
||||
stack_linkedList() { front = rear = NULL; }
|
||||
void enqueue(int);
|
||||
int dequeue();
|
||||
void display();
|
||||
};
|
||||
void stack_linkedList::enqueue(int ele) {
|
||||
linkedlist *temp = new linkedlist();
|
||||
temp->data = ele;
|
||||
temp->next = NULL;
|
||||
|
||||
if (front == NULL)
|
||||
front = rear = temp;
|
||||
else {
|
||||
rear->next = temp;
|
||||
rear = temp;
|
||||
}
|
||||
}
|
||||
int stack_linkedList::dequeue() {
|
||||
linkedlist *temp;
|
||||
int ele;
|
||||
if (front == NULL)
|
||||
std::cout << "\nStack is empty";
|
||||
else {
|
||||
temp = front;
|
||||
ele = temp->data;
|
||||
if (front == rear) // if length of queue is 1;
|
||||
rear = rear->next;
|
||||
front = front->next;
|
||||
delete (temp);
|
||||
}
|
||||
return ele;
|
||||
}
|
||||
void stack_linkedList::display() {
|
||||
if (front == NULL)
|
||||
std::cout << "\nStack is empty";
|
||||
|
||||
else {
|
||||
linkedlist *temp;
|
||||
temp = front;
|
||||
while (temp != NULL) {
|
||||
std::cout << temp->data << " ";
|
||||
temp = temp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int op, data;
|
||||
stack_linkedList ob;
|
||||
std::cout << "\n1. enqueue(Insertion) ";
|
||||
std::cout << "\n2. dequeue(Deletion)";
|
||||
std::cout << "\n3. Display";
|
||||
std::cout << "\n4. Exit";
|
||||
|
||||
while (1) {
|
||||
std::cout << "\nEnter your choice ";
|
||||
std::cin >> op;
|
||||
if (op == 1) {
|
||||
std::cout << "Enter data ";
|
||||
std::cin >> data;
|
||||
ob.enqueue(data);
|
||||
} else if (op == 2)
|
||||
data = ob.dequeue();
|
||||
else if (op == 3)
|
||||
ob.display();
|
||||
else if (op == 4)
|
||||
exit(0);
|
||||
else
|
||||
std::cout << "\nWrong choice ";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @author [shoniavika](https://github.com/shoniavika)
|
||||
* @file
|
||||
*
|
||||
* Implementation of a Queue using two Stacks.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* @brief Queue data structure. Stores elements in FIFO
|
||||
* (first-in-first-out) manner.
|
||||
* @tparam T datatype to store in the queue
|
||||
*/
|
||||
template <typename T>
|
||||
class MyQueue {
|
||||
private:
|
||||
std::stack<T> s1, s2;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor for queue.
|
||||
*/
|
||||
MyQueue() = default;
|
||||
|
||||
/**
|
||||
* Pushes x to the back of queue.
|
||||
*/
|
||||
void push(T x);
|
||||
|
||||
/**
|
||||
* Removes an element from the front of the queue.
|
||||
*/
|
||||
const T& pop();
|
||||
|
||||
/**
|
||||
* Returns first element, without removing it.
|
||||
*/
|
||||
const T& peek() const;
|
||||
|
||||
/**
|
||||
* Returns whether the queue is empty.
|
||||
*/
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Appends element to the end of the queue
|
||||
*/
|
||||
template <typename T>
|
||||
void MyQueue<T>::push(T x) {
|
||||
while (!s2.empty()) {
|
||||
s1.push(s2.top());
|
||||
s2.pop();
|
||||
}
|
||||
s2.push(x);
|
||||
while (!s1.empty()) {
|
||||
s2.push(s1.top());
|
||||
s1.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes element from the front of the queue
|
||||
*/
|
||||
template <typename T>
|
||||
const T& MyQueue<T>::pop() {
|
||||
const T& temp = MyQueue::peek();
|
||||
s2.pop();
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element in the front.
|
||||
* Does not remove it.
|
||||
*/
|
||||
template <typename T>
|
||||
const T& MyQueue<T>::peek() const {
|
||||
if (!empty()) {
|
||||
return s2.top();
|
||||
}
|
||||
std::cerr << "Queue is empty" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a queue is empty
|
||||
*/
|
||||
template <typename T>
|
||||
bool MyQueue<T>::empty() const {
|
||||
return s2.empty() && s1.empty();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* Testing function
|
||||
*/
|
||||
void queue_test() {
|
||||
MyQueue<int> que;
|
||||
std::cout << "Test #1\n";
|
||||
que.push(2);
|
||||
que.push(5);
|
||||
que.push(0);
|
||||
assert(que.peek() == 2);
|
||||
assert(que.pop() == 2);
|
||||
assert(que.peek() == 5);
|
||||
assert(que.pop() == 5);
|
||||
assert(que.peek() == 0);
|
||||
assert(que.pop() == 0);
|
||||
assert(que.empty() == true);
|
||||
std::cout << "PASSED\n";
|
||||
|
||||
std::cout << "Test #2\n";
|
||||
que.push(-1);
|
||||
assert(que.empty() == false);
|
||||
assert(que.peek() == -1);
|
||||
assert(que.pop() == -1);
|
||||
std::cout << "PASSED\n";
|
||||
|
||||
MyQueue<double> que2;
|
||||
std::cout << "Test #3\n";
|
||||
que2.push(2.31223);
|
||||
que2.push(3.1415926);
|
||||
que2.push(2.92);
|
||||
|
||||
assert(que2.peek() == 2.31223);
|
||||
assert(que2.pop() == 2.31223);
|
||||
assert(que2.peek() == 3.1415926);
|
||||
assert(que2.pop() == 3.1415926);
|
||||
assert(que2.peek() == 2.92);
|
||||
assert(que2.pop() == 2.92);
|
||||
std::cout << "PASSED\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function, calls testing function
|
||||
*/
|
||||
int main() {
|
||||
queue_test();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
#include<iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct node
|
||||
{
|
||||
int key;
|
||||
node *parent;
|
||||
char color;
|
||||
node *left;
|
||||
node *right;
|
||||
};
|
||||
class RBtree
|
||||
{
|
||||
node *root;
|
||||
node *q;
|
||||
public:
|
||||
RBtree()
|
||||
{
|
||||
q = NULL;
|
||||
root = NULL;
|
||||
}
|
||||
void insert();
|
||||
void insertfix(node *);
|
||||
void leftrotate(node *);
|
||||
void rightrotate(node *);
|
||||
void del();
|
||||
node* successor(node *);
|
||||
void delfix(node *);
|
||||
void disp();
|
||||
void display(node *);
|
||||
void search();
|
||||
};
|
||||
void RBtree::insert()
|
||||
{
|
||||
int z;
|
||||
cout << "\nEnter key of the node to be inserted: ";
|
||||
cin >> z;
|
||||
node *p, *q;
|
||||
node *t = new node;
|
||||
t->key = z;
|
||||
t->left = NULL;
|
||||
t->right = NULL;
|
||||
t->color = 'r';
|
||||
p = root;
|
||||
q = NULL;
|
||||
if (root == NULL)
|
||||
{
|
||||
root = t;
|
||||
t->parent = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (p != NULL)
|
||||
{
|
||||
q = p;
|
||||
if (p->key < t->key)
|
||||
p = p->right;
|
||||
else
|
||||
p = p->left;
|
||||
}
|
||||
t->parent = q;
|
||||
if (q->key < t->key)
|
||||
q->right = t;
|
||||
else
|
||||
q->left = t;
|
||||
}
|
||||
insertfix(t);
|
||||
}
|
||||
void RBtree::insertfix(node *t)
|
||||
{
|
||||
node *u;
|
||||
if (root == t)
|
||||
{
|
||||
t->color = 'b';
|
||||
return;
|
||||
}
|
||||
while (t->parent != NULL && t->parent->color == 'r')
|
||||
{
|
||||
node *g = t->parent->parent;
|
||||
if (g->left == t->parent)
|
||||
{
|
||||
if (g->right != NULL)
|
||||
{
|
||||
u = g->right;
|
||||
if (u->color == 'r')
|
||||
{
|
||||
t->parent->color = 'b';
|
||||
u->color = 'b';
|
||||
g->color = 'r';
|
||||
t = g;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->parent->right == t)
|
||||
{
|
||||
t = t->parent;
|
||||
leftrotate(t);
|
||||
}
|
||||
t->parent->color = 'b';
|
||||
g->color = 'r';
|
||||
rightrotate(g);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g->left != NULL)
|
||||
{
|
||||
u = g->left;
|
||||
if (u->color == 'r')
|
||||
{
|
||||
t->parent->color = 'b';
|
||||
u->color = 'b';
|
||||
g->color = 'r';
|
||||
t = g;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->parent->left == t)
|
||||
{
|
||||
t = t->parent;
|
||||
rightrotate(t);
|
||||
}
|
||||
t->parent->color = 'b';
|
||||
g->color = 'r';
|
||||
leftrotate(g);
|
||||
}
|
||||
}
|
||||
root->color = 'b';
|
||||
}
|
||||
}
|
||||
|
||||
void RBtree::del()
|
||||
{
|
||||
if (root == NULL)
|
||||
{
|
||||
cout << "\nEmpty Tree.";
|
||||
return;
|
||||
}
|
||||
int x;
|
||||
cout << "\nEnter the key of the node to be deleted: ";
|
||||
cin >> x;
|
||||
node *p;
|
||||
p = root;
|
||||
node *y = NULL;
|
||||
node *q = NULL;
|
||||
int found = 0;
|
||||
while (p != NULL && found == 0)
|
||||
{
|
||||
if (p->key == x)
|
||||
found = 1;
|
||||
if (found == 0)
|
||||
{
|
||||
if (p->key < x)
|
||||
p = p->right;
|
||||
else
|
||||
p = p->left;
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
{
|
||||
cout << "\nElement Not Found.";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "\nDeleted Element: " << p->key;
|
||||
cout << "\nColour: ";
|
||||
if (p->color == 'b')
|
||||
cout << "Black\n";
|
||||
else
|
||||
cout << "Red\n";
|
||||
|
||||
if (p->parent != NULL)
|
||||
cout << "\nParent: " << p->parent->key;
|
||||
else
|
||||
cout << "\nThere is no parent of the node. ";
|
||||
if (p->right != NULL)
|
||||
cout << "\nRight Child: " << p->right->key;
|
||||
else
|
||||
cout << "\nThere is no right child of the node. ";
|
||||
if (p->left != NULL)
|
||||
cout << "\nLeft Child: " << p->left->key;
|
||||
else
|
||||
cout << "\nThere is no left child of the node. ";
|
||||
cout << "\nNode Deleted.";
|
||||
if (p->left == NULL || p->right == NULL)
|
||||
y = p;
|
||||
else
|
||||
y = successor(p);
|
||||
if (y->left != NULL)
|
||||
q = y->left;
|
||||
else
|
||||
{
|
||||
if (y->right != NULL)
|
||||
q = y->right;
|
||||
else
|
||||
q = NULL;
|
||||
}
|
||||
if (q != NULL)
|
||||
q->parent = y->parent;
|
||||
if (y->parent == NULL)
|
||||
root = q;
|
||||
else
|
||||
{
|
||||
if (y == y->parent->left)
|
||||
y->parent->left = q;
|
||||
else
|
||||
y->parent->right = q;
|
||||
}
|
||||
if (y != p)
|
||||
{
|
||||
p->color = y->color;
|
||||
p->key = y->key;
|
||||
}
|
||||
if (y->color == 'b')
|
||||
delfix(q);
|
||||
}
|
||||
}
|
||||
|
||||
void RBtree::delfix(node *p)
|
||||
{
|
||||
node *s;
|
||||
while (p != root && p->color == 'b')
|
||||
{
|
||||
if (p->parent->left == p)
|
||||
{
|
||||
s = p->parent->right;
|
||||
if (s->color == 'r')
|
||||
{
|
||||
s->color = 'b';
|
||||
p->parent->color = 'r';
|
||||
leftrotate(p->parent);
|
||||
s = p->parent->right;
|
||||
}
|
||||
if (s->right->color == 'b'&&s->left->color == 'b')
|
||||
{
|
||||
s->color = 'r';
|
||||
p = p->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s->right->color == 'b')
|
||||
{
|
||||
s->left->color = 'b';
|
||||
s->color = 'r';
|
||||
rightrotate(s);
|
||||
s = p->parent->right;
|
||||
}
|
||||
s->color = p->parent->color;
|
||||
p->parent->color = 'b';
|
||||
s->right->color = 'b';
|
||||
leftrotate(p->parent);
|
||||
p = root;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s = p->parent->left;
|
||||
if (s->color == 'r')
|
||||
{
|
||||
s->color = 'b';
|
||||
p->parent->color = 'r';
|
||||
rightrotate(p->parent);
|
||||
s = p->parent->left;
|
||||
}
|
||||
if (s->left->color == 'b'&&s->right->color == 'b')
|
||||
{
|
||||
s->color = 'r';
|
||||
p = p->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s->left->color == 'b')
|
||||
{
|
||||
s->right->color = 'b';
|
||||
s->color = 'r';
|
||||
leftrotate(s);
|
||||
s = p->parent->left;
|
||||
}
|
||||
s->color = p->parent->color;
|
||||
p->parent->color = 'b';
|
||||
s->left->color = 'b';
|
||||
rightrotate(p->parent);
|
||||
p = root;
|
||||
}
|
||||
}
|
||||
p->color = 'b';
|
||||
root->color = 'b';
|
||||
}
|
||||
}
|
||||
|
||||
void RBtree::leftrotate(node *p)
|
||||
{
|
||||
if (p->right == NULL)
|
||||
return;
|
||||
else
|
||||
{
|
||||
node *y = p->right;
|
||||
if (y->left != NULL)
|
||||
{
|
||||
p->right = y->left;
|
||||
y->left->parent = p;
|
||||
}
|
||||
else
|
||||
p->right = NULL;
|
||||
if (p->parent != NULL)
|
||||
y->parent = p->parent;
|
||||
if (p->parent == NULL)
|
||||
root = y;
|
||||
else
|
||||
{
|
||||
if (p == p->parent->left)
|
||||
p->parent->left = y;
|
||||
else
|
||||
p->parent->right = y;
|
||||
}
|
||||
y->left = p;
|
||||
p->parent = y;
|
||||
}
|
||||
}
|
||||
void RBtree::rightrotate(node *p)
|
||||
{
|
||||
if (p->left == NULL)
|
||||
return;
|
||||
else
|
||||
{
|
||||
node *y = p->left;
|
||||
if (y->right != NULL)
|
||||
{
|
||||
p->left = y->right;
|
||||
y->right->parent = p;
|
||||
}
|
||||
else
|
||||
p->left = NULL;
|
||||
if (p->parent != NULL)
|
||||
y->parent = p->parent;
|
||||
if (p->parent == NULL)
|
||||
root = y;
|
||||
else
|
||||
{
|
||||
if (p == p->parent->left)
|
||||
p->parent->left = y;
|
||||
else
|
||||
p->parent->right = y;
|
||||
}
|
||||
y->right = p;
|
||||
p->parent = y;
|
||||
}
|
||||
}
|
||||
|
||||
node* RBtree::successor(node *p)
|
||||
{
|
||||
node *y = NULL;
|
||||
if (p->left != NULL)
|
||||
{
|
||||
y = p->left;
|
||||
while (y->right != NULL)
|
||||
y = y->right;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = p->right;
|
||||
while (y->left != NULL)
|
||||
y = y->left;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
void RBtree::disp()
|
||||
{
|
||||
display(root);
|
||||
}
|
||||
void RBtree::display(node *p)
|
||||
{
|
||||
if (root == NULL)
|
||||
{
|
||||
cout << "\nEmpty Tree.";
|
||||
return;
|
||||
}
|
||||
if (p != NULL)
|
||||
{
|
||||
cout << "\n\t NODE: ";
|
||||
cout << "\n Key: " << p->key;
|
||||
cout << "\n Colour: ";
|
||||
if (p->color == 'b')
|
||||
cout << "Black";
|
||||
else
|
||||
cout << "Red";
|
||||
if (p->parent != NULL)
|
||||
cout << "\n Parent: " << p->parent->key;
|
||||
else
|
||||
cout << "\n There is no parent of the node. ";
|
||||
if (p->right != NULL)
|
||||
cout << "\n Right Child: " << p->right->key;
|
||||
else
|
||||
cout << "\n There is no right child of the node. ";
|
||||
if (p->left != NULL)
|
||||
cout << "\n Left Child: " << p->left->key;
|
||||
else
|
||||
cout << "\n There is no left child of the node. ";
|
||||
cout << endl;
|
||||
if (p->left)
|
||||
{
|
||||
cout << "\n\nLeft:\n";
|
||||
display(p->left);
|
||||
}
|
||||
/*else
|
||||
cout<<"\nNo Left Child.\n";*/
|
||||
if (p->right)
|
||||
{
|
||||
cout << "\n\nRight:\n";
|
||||
display(p->right);
|
||||
}
|
||||
/*else
|
||||
cout<<"\nNo Right Child.\n"*/
|
||||
}
|
||||
}
|
||||
void RBtree::search()
|
||||
{
|
||||
if (root == NULL)
|
||||
{
|
||||
cout << "\nEmpty Tree\n";
|
||||
return;
|
||||
}
|
||||
int x;
|
||||
cout << "\n Enter key of the node to be searched: ";
|
||||
cin >> x;
|
||||
node *p = root;
|
||||
int found = 0;
|
||||
while (p != NULL && found == 0)
|
||||
{
|
||||
if (p->key == x)
|
||||
found = 1;
|
||||
if (found == 0)
|
||||
{
|
||||
if (p->key < x)
|
||||
p = p->right;
|
||||
else
|
||||
p = p->left;
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
cout << "\nElement Not Found.";
|
||||
else
|
||||
{
|
||||
cout << "\n\t FOUND NODE: ";
|
||||
cout << "\n Key: " << p->key;
|
||||
cout << "\n Colour: ";
|
||||
if (p->color == 'b')
|
||||
cout << "Black";
|
||||
else
|
||||
cout << "Red";
|
||||
if (p->parent != NULL)
|
||||
cout << "\n Parent: " << p->parent->key;
|
||||
else
|
||||
cout << "\n There is no parent of the node. ";
|
||||
if (p->right != NULL)
|
||||
cout << "\n Right Child: " << p->right->key;
|
||||
else
|
||||
cout << "\n There is no right child of the node. ";
|
||||
if (p->left != NULL)
|
||||
cout << "\n Left Child: " << p->left->key;
|
||||
else
|
||||
cout << "\n There is no left child of the node. ";
|
||||
cout << endl;
|
||||
|
||||
}
|
||||
}
|
||||
int main()
|
||||
{
|
||||
int ch, y = 0;
|
||||
RBtree obj;
|
||||
do
|
||||
{
|
||||
cout << "\n\t RED BLACK TREE ";
|
||||
cout << "\n 1. Insert in the tree ";
|
||||
cout << "\n 2. Delete a node from the tree";
|
||||
cout << "\n 3. Search for an element in the tree";
|
||||
cout << "\n 4. Display the tree ";
|
||||
cout << "\n 5. Exit ";
|
||||
cout << "\nEnter Your Choice: ";
|
||||
cin >> ch;
|
||||
switch (ch)
|
||||
{
|
||||
case 1: obj.insert();
|
||||
cout << "\nNode Inserted.\n";
|
||||
break;
|
||||
case 2: obj.del();
|
||||
break;
|
||||
case 3: obj.search();
|
||||
break;
|
||||
case 4: obj.disp();
|
||||
break;
|
||||
case 5: y = 1;
|
||||
break;
|
||||
default: cout << "\nEnter a Valid Choice.";
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
} while (y != 1);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Implementation of [Reversing
|
||||
* a single linked list](https://simple.wikipedia.org/wiki/Linked_list)
|
||||
* @details
|
||||
* The linked list is a data structure used for holding a sequence of
|
||||
* values, which can be added, displayed, reversed, or removed.
|
||||
* ### Algorithm
|
||||
* Values can be added by iterating to the end of a list (by following
|
||||
* the pointers) starting from the first link. Whichever link points to null
|
||||
* is considered the last link and is pointed to the new value.
|
||||
*
|
||||
* Linked List can be reversed by using 3 pointers: current, previous, and
|
||||
* next_node; we keep iterating until the last node. Meanwhile, before changing
|
||||
* to the next of current, we store it in the next_node pointer, now we store
|
||||
* the prev pointer in the current of next, this is where the actual reversal
|
||||
* happens. And then we move the prev and current pointers one step forward.
|
||||
* Then the head node is made to point to the last node (prev pointer) after
|
||||
* completion of an iteration.
|
||||
|
||||
* [A graphic explanation and view of what's happening behind the
|
||||
*scenes](https://drive.google.com/file/d/1pM5COF0wx-wermnNy_svtyZquaCUP2xS/view?usp=sharing)
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for I/O operations
|
||||
#include <new> /// for managing dynamic storage
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data Structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @namespace linked_list
|
||||
* @brief Functions for singly linked list algorithm
|
||||
*/
|
||||
namespace linked_list {
|
||||
/**
|
||||
* A Node class containing a value and pointer to another link
|
||||
*/
|
||||
class Node {
|
||||
public:
|
||||
int32_t val; /// value of the current link
|
||||
Node* next; /// pointer to the next value on the list
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief creates a deep copy of a list starting at the input node
|
||||
* @param[in] node pointer to the first node/head of the list to be copied
|
||||
* @return pointer to the first node/head of the copied list or nullptr
|
||||
*/
|
||||
Node* copy_all_nodes(const Node* const node) {
|
||||
if (node) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
|
||||
Node* res = new Node();
|
||||
res->val = node->val;
|
||||
res->next = copy_all_nodes(node->next);
|
||||
return res;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list class containing a sequence of links
|
||||
*/
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
||||
class list {
|
||||
private:
|
||||
Node* head = nullptr; // link before the actual first element
|
||||
void delete_all_nodes();
|
||||
void copy_all_nodes_from_list(const list& other);
|
||||
|
||||
public:
|
||||
bool isEmpty() const;
|
||||
void insert(int32_t new_elem);
|
||||
void reverseList();
|
||||
void display() const;
|
||||
int32_t top() const;
|
||||
int32_t last() const;
|
||||
int32_t traverse(int32_t index) const;
|
||||
~list();
|
||||
list() = default;
|
||||
list(const list& other);
|
||||
list& operator=(const list& other);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility function that checks if the list is empty
|
||||
* @returns true if the list is empty
|
||||
* @returns false if the list is not empty
|
||||
*/
|
||||
bool list::isEmpty() const { return head == nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Utility function that adds a new element at the end of the list
|
||||
* @param new_elem element be added at the end of the list
|
||||
*/
|
||||
void list::insert(int32_t n) {
|
||||
try {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
|
||||
Node* new_node = new Node();
|
||||
Node* temp = nullptr;
|
||||
new_node->val = n;
|
||||
new_node->next = nullptr;
|
||||
if (isEmpty()) {
|
||||
head = new_node;
|
||||
} else {
|
||||
temp = head;
|
||||
while (temp->next != nullptr) {
|
||||
temp = temp->next;
|
||||
}
|
||||
temp->next = new_node;
|
||||
}
|
||||
} catch (std::bad_alloc& exception) {
|
||||
std::cerr << "bad_alloc detected: " << exception.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function for reversing a list
|
||||
* @brief Using the current, previous, and next pointer.
|
||||
* @returns void
|
||||
*/
|
||||
void list::reverseList() {
|
||||
Node* curr = head;
|
||||
Node* prev = nullptr;
|
||||
Node* next_node = nullptr;
|
||||
while (curr != nullptr) {
|
||||
next_node = curr->next;
|
||||
curr->next = prev;
|
||||
prev = curr;
|
||||
curr = next_node;
|
||||
}
|
||||
head = prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to find the top element of the list
|
||||
* @returns the top element of the list
|
||||
*/
|
||||
int32_t list::top() const {
|
||||
if (!isEmpty()) {
|
||||
return head->val;
|
||||
} else {
|
||||
throw std::logic_error("List is empty");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Utility function to find the last element of the list
|
||||
* @returns the last element of the list
|
||||
*/
|
||||
int32_t list::last() const {
|
||||
if (!isEmpty()) {
|
||||
Node* t = head;
|
||||
while (t->next != nullptr) {
|
||||
t = t->next;
|
||||
}
|
||||
return t->val;
|
||||
} else {
|
||||
throw std::logic_error("List is empty");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Utility function to find the i th element of the list
|
||||
* @returns the i th element of the list
|
||||
*/
|
||||
int32_t list::traverse(int32_t index) const {
|
||||
Node* current = head;
|
||||
|
||||
int count = 0;
|
||||
while (current != nullptr) {
|
||||
if (count == index) {
|
||||
return (current->val);
|
||||
}
|
||||
count++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
/* if we get to this line,the caller was asking for a non-existent element
|
||||
so we assert fail */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief calls delete operator on every node in the represented list
|
||||
*/
|
||||
void list::delete_all_nodes() {
|
||||
while (head != nullptr) {
|
||||
const auto tmp_node = head->next;
|
||||
delete head;
|
||||
head = tmp_node;
|
||||
}
|
||||
}
|
||||
|
||||
list::~list() { delete_all_nodes(); }
|
||||
|
||||
void list::copy_all_nodes_from_list(const list& other) {
|
||||
assert(isEmpty());
|
||||
head = copy_all_nodes(other.head);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief copy constructor creating a deep copy of every node of the input
|
||||
*/
|
||||
list::list(const list& other) { copy_all_nodes_from_list(other); }
|
||||
|
||||
/**
|
||||
* @brief assignment operator creating a deep copy of every node of the input
|
||||
*/
|
||||
list& list::operator=(const list& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
delete_all_nodes();
|
||||
|
||||
copy_all_nodes_from_list(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace linked_list
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::linked_list::list L;
|
||||
// 1st test
|
||||
L.insert(11);
|
||||
L.insert(12);
|
||||
L.insert(15);
|
||||
L.insert(10);
|
||||
L.insert(-12);
|
||||
L.insert(-20);
|
||||
L.insert(18);
|
||||
assert(L.top() == 11);
|
||||
assert(L.last() == 18);
|
||||
L.reverseList();
|
||||
// Reversal Testing
|
||||
assert(L.top() == 18);
|
||||
assert(L.traverse(1) == -20);
|
||||
assert(L.traverse(2) == -12);
|
||||
assert(L.traverse(3) == 10);
|
||||
assert(L.traverse(4) == 15);
|
||||
assert(L.traverse(5) == 12);
|
||||
assert(L.last() == 11);
|
||||
std::cout << "All tests have successfully passed!" << std::endl;
|
||||
}
|
||||
|
||||
void test_copy_constructor() {
|
||||
data_structures::linked_list::list L;
|
||||
L.insert(10);
|
||||
L.insert(20);
|
||||
L.insert(30);
|
||||
data_structures::linked_list::list otherList(L);
|
||||
otherList.insert(40);
|
||||
|
||||
L.insert(400);
|
||||
|
||||
assert(L.top() == 10);
|
||||
assert(otherList.top() == 10);
|
||||
assert(L.traverse(1) == 20);
|
||||
assert(otherList.traverse(1) == 20);
|
||||
|
||||
assert(L.traverse(2) == 30);
|
||||
assert(otherList.traverse(2) == 30);
|
||||
|
||||
assert(L.last() == 400);
|
||||
assert(otherList.last() == 40);
|
||||
}
|
||||
|
||||
void test_assignment_operator() {
|
||||
data_structures::linked_list::list L;
|
||||
data_structures::linked_list::list otherList;
|
||||
L.insert(10);
|
||||
L.insert(20);
|
||||
L.insert(30);
|
||||
otherList = L;
|
||||
|
||||
otherList.insert(40);
|
||||
L.insert(400);
|
||||
|
||||
assert(L.top() == 10);
|
||||
assert(otherList.top() == 10);
|
||||
assert(L.traverse(1) == 20);
|
||||
assert(otherList.traverse(1) == 20);
|
||||
|
||||
assert(L.traverse(2) == 30);
|
||||
assert(otherList.traverse(2) == 30);
|
||||
|
||||
assert(L.last() == 400);
|
||||
assert(otherList.last() == 40);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
test_copy_constructor();
|
||||
test_assignment_operator();
|
||||
return 0;
|
||||
}
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A data structure to quickly do operations on ranges: the [Segment Tree](https://en.wikipedia.org/wiki/Segment_tree) algorithm implementation
|
||||
* @details
|
||||
* Implementation of the segment tree data structre
|
||||
*
|
||||
* Can do point updates (updates the value of some position)
|
||||
* and range queries, where it gives the value of some associative
|
||||
* opperation done on a range
|
||||
*
|
||||
* Both of these operations take O(log N) time
|
||||
* @author [Nishant Chatterjee](https://github.com/nishantc1527)
|
||||
*/
|
||||
|
||||
#include <iostream> /// For IO operations
|
||||
#include <vector> /// For std::vector
|
||||
#include <algorithm> /// For std::min and std::max
|
||||
#include <cassert> /// For assert
|
||||
|
||||
/*
|
||||
* @namespace
|
||||
* @brief Data structures
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @brief class representation of the segment tree
|
||||
* @tparam T The type of the class that goes in the datastructure
|
||||
*/
|
||||
template <class T>
|
||||
class SegmentTree {
|
||||
private:
|
||||
const T ID = 0; ///< Comb(ID, x) = x
|
||||
std::vector<T> t; ///< Vector to represent the tree
|
||||
int size = 0; ///< Number of elements available for querying in the tree
|
||||
private:
|
||||
/**
|
||||
* @brief Any associative function that combines x and y
|
||||
* @param x The first operand
|
||||
* @param y The second operand
|
||||
* @return Some associative operation applied to these two values. In this case, I used addition
|
||||
*/
|
||||
T comb(T x, T y) {
|
||||
return x + y;
|
||||
}
|
||||
/**
|
||||
* @brief Gives the midpoint between two integers
|
||||
* @param l The left endpoint
|
||||
* @param r The right endpoint
|
||||
* @return the middle point between them
|
||||
*/
|
||||
int mid(int l, int r) {
|
||||
return l + (r - l) / 2;
|
||||
}
|
||||
/**
|
||||
* @brief Helper method for update method below
|
||||
* @param i The index of the current node
|
||||
* @param l The leftmost node of the current node
|
||||
* @param r The rightmost node of the current node
|
||||
* @param pos The position to update
|
||||
* @param val The value to update it to
|
||||
*/
|
||||
void update(int i, int l, int r, int pos, T val) {
|
||||
if(l == r) t[i] = val;
|
||||
else {
|
||||
int m = mid(l, r);
|
||||
if(pos <= m) update(i * 2, l, m, pos, val);
|
||||
else update(i * 2 + 1, m + 1, r, pos, val);
|
||||
t[i] = comb(t[i * 2], t[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Helper method for range_comb method below
|
||||
* @param i The current node
|
||||
* @param l The leftmost node of the current node
|
||||
* @param r The rightmost node of the current node
|
||||
* @param tl The left endpoint of the range
|
||||
* @param tr The right endpoint of the range
|
||||
* @return The comb operation applied to all values between tl and tr
|
||||
*/
|
||||
T range_comb(int i, int l, int r, int tl, int tr) {
|
||||
if(l == tl && r == tr) return t[i];
|
||||
if(tl > tr) return 0;
|
||||
int m = mid(l, r);
|
||||
return comb(range_comb(i * 2, l, m, tl, std::min(tr, m)), range_comb(i * 2 + 1, m + 1, r, std::max(tl, m + 1), tr));
|
||||
}
|
||||
public:
|
||||
SegmentTree(int n) : t(n * 4, ID), size(n) {}
|
||||
/**
|
||||
* @brief Updates a value at a certain position
|
||||
* @param pos The position to update
|
||||
* @param val The value to update it to
|
||||
*/
|
||||
void update(int pos, T val) {
|
||||
update(1, 1, size, pos, val);
|
||||
}
|
||||
/**
|
||||
* @brief Returns comb across all values between l and r
|
||||
* @param l The left endpoint of the range
|
||||
* @param r The right endpoint of the range
|
||||
* @return The value of the comb operations
|
||||
*/
|
||||
T range_comb(int l, int r) {
|
||||
return range_comb(1, 1, size, l, r);
|
||||
}
|
||||
};
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::SegmentTree<int> t(5);
|
||||
t.update(1, 1);
|
||||
t.update(2, 2);
|
||||
t.update(3, 3);
|
||||
t.update(4, 4);
|
||||
t.update(5, 5);
|
||||
assert(t.range_comb(1, 3) == 6); // 1 + 2 + 3 = 6
|
||||
t.update(1, 3);
|
||||
assert(t.range_comb(1, 3) == 8); // 3 + 2 + 3 = 8
|
||||
|
||||
std::cout << "All tests have successfully passed!\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* @file skip_list.cpp
|
||||
* @brief Data structure for fast searching and insertion in \f$O(\log n)\f$
|
||||
* time
|
||||
* @details
|
||||
* A skip list is a data structure that is used for storing a sorted list of
|
||||
* items with a help of hierarchy of linked lists that connect increasingly
|
||||
* sparse subsequences of the items
|
||||
*
|
||||
* References used: [GeeksForGeek](https://www.geeksforgeeks.org/skip-list/),
|
||||
* [OpenGenus](https://iq.opengenus.org/skip-list) for PseudoCode and Code
|
||||
* @author [enqidu](https://github.com/enqidu)
|
||||
* @author [Krishna Vedala](https://github.com/kvedala)
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/** \namespace data_structures
|
||||
* \brief Data-structure algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
constexpr int MAX_LEVEL = 2; ///< Maximum level of skip list
|
||||
constexpr float PROBABILITY = 0.5; ///< Current probability for "coin toss"
|
||||
|
||||
/**
|
||||
* Node structure [Key][Node*, Node*...]
|
||||
*/
|
||||
struct Node {
|
||||
int key; ///< key integer
|
||||
void* value; ///< pointer of value
|
||||
std::vector<std::shared_ptr<Node>>
|
||||
forward; ///< nodes of the given one in all levels
|
||||
|
||||
/**
|
||||
* Creates node with provided key, level and value
|
||||
* @param key is number that is used for comparision
|
||||
* @param level is the maximum level node's going to added
|
||||
*/
|
||||
Node(int key, int level, void* value = nullptr) : key(key), value(value) {
|
||||
// Initialization of forward vector
|
||||
for (int i = 0; i < (level + 1); i++) {
|
||||
forward.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SkipList class implementation with basic methods
|
||||
*/
|
||||
class SkipList {
|
||||
int level; ///< Maximum level of the skiplist
|
||||
std::shared_ptr<Node> header; ///< Pointer to the header node
|
||||
|
||||
public:
|
||||
/**
|
||||
* Skip List constructor. Initializes header, start
|
||||
* Node for searching in the list
|
||||
*/
|
||||
SkipList() {
|
||||
level = 0;
|
||||
// Header initialization
|
||||
header = std::make_shared<Node>(-1, MAX_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random level of the skip list.
|
||||
* Every higher level is 2 times less likely.
|
||||
* @return random level for skip list
|
||||
*/
|
||||
int randomLevel() {
|
||||
int lvl = 0;
|
||||
while (static_cast<float>(std::rand()) / RAND_MAX < PROBABILITY &&
|
||||
lvl < MAX_LEVEL) {
|
||||
lvl++;
|
||||
}
|
||||
return lvl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts elements with given key and value;
|
||||
* It's level is computed by randomLevel() function.
|
||||
* @param key is number that is used for comparision
|
||||
* @param value pointer to a value, that can be any type
|
||||
*/
|
||||
void insertElement(int key, void* value) {
|
||||
std::cout << "Inserting" << key << "...";
|
||||
std::shared_ptr<Node> x = header;
|
||||
std::array<std::shared_ptr<Node>, MAX_LEVEL + 1> update;
|
||||
update.fill(nullptr);
|
||||
|
||||
for (int i = level; i >= 0; i--) {
|
||||
while (x->forward[i] != nullptr && x->forward[i]->key < key) {
|
||||
x = x->forward[i];
|
||||
}
|
||||
update[i] = x;
|
||||
}
|
||||
|
||||
x = x->forward[0];
|
||||
|
||||
bool doesnt_exist = (x == nullptr || x->key != key);
|
||||
if (doesnt_exist) {
|
||||
int rlevel = randomLevel();
|
||||
|
||||
if (rlevel > level) {
|
||||
for (int i = level + 1; i < rlevel + 1; i++) update[i] = header;
|
||||
|
||||
// Update current level
|
||||
level = rlevel;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> n =
|
||||
std::make_shared<Node>(key, rlevel, value);
|
||||
for (int i = 0; i <= rlevel; i++) {
|
||||
n->forward[i] = update[i]->forward[i];
|
||||
update[i]->forward[i] = n;
|
||||
}
|
||||
std::cout << "Inserted" << std::endl;
|
||||
|
||||
} else {
|
||||
std::cout << "Exists" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an element by key and prints if has been removed successfully
|
||||
* @param key is number that is used for comparision.
|
||||
*/
|
||||
void deleteElement(int key) {
|
||||
std::shared_ptr<Node> x = header;
|
||||
|
||||
std::array<std::shared_ptr<Node>, MAX_LEVEL + 1> update;
|
||||
update.fill(nullptr);
|
||||
|
||||
for (int i = level; i >= 0; i--) {
|
||||
while (x->forward[i] != nullptr && x->forward[i]->key < key) {
|
||||
x = x->forward[i];
|
||||
}
|
||||
update[i] = x;
|
||||
}
|
||||
|
||||
x = x->forward[0];
|
||||
|
||||
bool doesnt_exist = (x == nullptr || x->key != key);
|
||||
|
||||
if (!doesnt_exist) {
|
||||
for (int i = 0; i <= level; i++) {
|
||||
if (update[i]->forward[i] != x) {
|
||||
break;
|
||||
}
|
||||
update[i]->forward[i] = x->forward[i];
|
||||
}
|
||||
/* Remove empty levels*/
|
||||
while (level > 0 && header->forward[level] == nullptr) level--;
|
||||
std::cout << "Deleted" << std::endl;
|
||||
} else {
|
||||
std::cout << "Doesn't exist" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searching element in skip list structure
|
||||
* @param key is number that is used for comparision
|
||||
* @return pointer to the value of the node
|
||||
*/
|
||||
void* searchElement(int key) {
|
||||
std::shared_ptr<Node> x = header;
|
||||
std::cout << "Searching for " << key << std::endl;
|
||||
|
||||
for (int i = level; i >= 0; i--) {
|
||||
while (x->forward[i] && x->forward[i]->key < key) x = x->forward[i];
|
||||
}
|
||||
|
||||
x = x->forward[0];
|
||||
if (x && x->key == key) {
|
||||
std::cout << "Found" << std::endl;
|
||||
return x->value;
|
||||
} else {
|
||||
std::cout << "Not Found" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display skip list level
|
||||
*/
|
||||
void displayList() {
|
||||
std::cout << "Displaying list:\n";
|
||||
for (int i = 0; i <= level; i++) {
|
||||
std::shared_ptr<Node> node = header->forward[i];
|
||||
std::cout << "Level " << (i) << ": ";
|
||||
while (node != nullptr) {
|
||||
std::cout << node->key << " ";
|
||||
node = node->forward[i];
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* Main function:
|
||||
* Creates and inserts random 2^[number of levels]
|
||||
* elements into the skip lists and than displays it
|
||||
*/
|
||||
int main() {
|
||||
std::srand(std::time(nullptr));
|
||||
|
||||
data_structures::SkipList lst;
|
||||
|
||||
for (int j = 0; j < (1 << (data_structures::MAX_LEVEL + 1)); j++) {
|
||||
int k = (std::rand() % (1 << (data_structures::MAX_LEVEL + 2)) + 1);
|
||||
lst.insertElement(k, &j);
|
||||
}
|
||||
|
||||
lst.displayList();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Implementation of [Sparse
|
||||
* Table](https://brilliant.org/wiki/sparse-table/) for `min()` function.
|
||||
* @author [Mann Patel](https://github.com/manncodes)
|
||||
* @details
|
||||
* Sparse Table is a data structure, that allows answering range queries.
|
||||
* It can answer most range queries in O(logn), but its true power is answering
|
||||
* range minimum queries (or equivalent range maximum queries). For those
|
||||
* queries it can compute the answer in O(1) time. The only drawback of this
|
||||
* data structure is, that it can only be used on immutable arrays. This means,
|
||||
* that the array cannot be changed between two queries.
|
||||
*
|
||||
* If any element in the array changes, the complete data structure has to be
|
||||
* recomputed.
|
||||
*
|
||||
* @todo make stress tests.
|
||||
*
|
||||
* @warning
|
||||
* This sparse table is made for `min(a1,a2,...an)` duplicate invariant
|
||||
* function. This implementation can be changed to other functions like
|
||||
* `gcd()`, `lcm()`, and `max()` by changing a few lines of code.
|
||||
*/
|
||||
|
||||
#include <array> /// for std::array
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for IO operations
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data Structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
|
||||
/**
|
||||
* @namespace sparse_table
|
||||
* @brief Functions for Implementation of [Sparse
|
||||
* Table](https://brilliant.org/wiki/sparse-table/)
|
||||
*/
|
||||
namespace sparse_table {
|
||||
|
||||
/**
|
||||
* @brief A struct to represent sparse table for `min()` as their invariant
|
||||
* function, for the given array `A`. The answer to queries are stored in the
|
||||
* array ST.
|
||||
*/
|
||||
constexpr uint32_t N = 12345; ///< the maximum size of the array.
|
||||
constexpr uint8_t M = 14; ///< ceil(log2(N)).
|
||||
|
||||
struct Sparse_table {
|
||||
size_t n = 0; ///< size of input array.
|
||||
|
||||
/** @warning check if `N` is not less than `n`. if so, manually increase the
|
||||
* value of N */
|
||||
|
||||
std::array<int64_t, N> A = {}; ///< input array to perform RMQ.
|
||||
std::array<std::array<int64_t, N>, M>
|
||||
ST{}; ///< the sparse table storing `min()` values for given interval.
|
||||
std::array<int64_t, N> LOG = {}; ///< where floor(log2(i)) are precomputed.
|
||||
|
||||
/**
|
||||
* @brief Builds the sparse table for computing min/max/gcd/lcm/...etc
|
||||
* for any contiguous sub-segment of the array.This is an example of
|
||||
* computing the index of the minimum value.
|
||||
* @return void
|
||||
* @complexity: O(n.log(n))
|
||||
*/
|
||||
void buildST() {
|
||||
LOG[0] = -1;
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
ST[0][i] = static_cast<int64_t>(i);
|
||||
LOG[i + 1] = LOG[i] + !(i & (i + 1)); ///< precomputing `log2(i+1)`
|
||||
}
|
||||
|
||||
for (size_t j = 1; static_cast<size_t>(1 << j) <= n; ++j) {
|
||||
for (size_t i = 0; static_cast<size_t>(i + (1 << j)) <= n; ++i) {
|
||||
/**
|
||||
* @note notice how we deal with the range of length `pow(2,i)`,
|
||||
* and we can reuse the computation that we did for the range of
|
||||
* length `pow(2,i-1)`.
|
||||
*
|
||||
* So, ST[j][i] = min( ST[j-1][i], ST[j-1][i + pow(2,j-1)]).
|
||||
* @example ST[2][3] = min(ST[1][3], ST[1][5])
|
||||
*/
|
||||
|
||||
int64_t x = ST[j - 1][i]; ///< represents minimum value over
|
||||
///< the range [j,i]
|
||||
int64_t y =
|
||||
ST[j - 1]
|
||||
[i + (1 << (j - 1))]; ///< represents minimum value over
|
||||
///< the range [j,i + pow(2,j-1)]
|
||||
|
||||
ST[j][i] =
|
||||
(A[x] <= A[y] ? x : y); ///< represents minimum value over
|
||||
///< the range [j,i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queries the sparse table for the value of the interval [l, r]
|
||||
* (i.e. from l to r inclusive).
|
||||
* @param l the left index of the range (inclusive).
|
||||
* @param r the right index of the range (inclusive).
|
||||
* @return the computed value of the given interval.
|
||||
* @complexity: O(1)
|
||||
*/
|
||||
int64_t query(int64_t l, int64_t r) {
|
||||
int64_t g = LOG[r - l + 1]; ///< smallest power of 2 covering [l,r]
|
||||
int64_t x = ST[g][l]; ///< represents minimum value over the range
|
||||
///< [g,l]
|
||||
int64_t y =
|
||||
ST[g][r - (1 << g) + 1]; ///< represents minimum value over the
|
||||
///< range [g, r - pow(2,g) + 1]
|
||||
|
||||
return (A[x] <= A[y] ? x : y); ///< represents minimum value over
|
||||
///< the whole range [l,r]
|
||||
}
|
||||
};
|
||||
} // namespace sparse_table
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
/* We take an array as an input on which we need to perform the ranged
|
||||
* minimum queries[RMQ](https://en.wikipedia.org/wiki/Range_minimum_query).
|
||||
*/
|
||||
std::array<int64_t, 10> testcase = {
|
||||
1, 2, 3, 4, 5,
|
||||
6, 7, 8, 9, 10}; ///< array on which RMQ will be performed.
|
||||
size_t testcase_size =
|
||||
sizeof(testcase) / sizeof(testcase[0]); ///< size of self test's array
|
||||
|
||||
data_structures::sparse_table::Sparse_table
|
||||
st{}; ///< declaring sparse tree
|
||||
|
||||
std::copy(std::begin(testcase), std::end(testcase),
|
||||
std::begin(st.A)); ///< copying array to the struct
|
||||
st.n = testcase_size; ///< passing the array's size to the struct
|
||||
|
||||
st.buildST(); ///< precomputing sparse tree
|
||||
|
||||
// pass queries of the form: [l,r]
|
||||
assert(st.query(1, 9) == 1); ///< as 1 is smallest from 1..9
|
||||
assert(st.query(2, 6) == 2); ///< as 2 is smallest from 2..6
|
||||
assert(st.query(3, 8) == 3); ///< as 3 is smallest from 3..8
|
||||
|
||||
std::cout << "Self-test implementations passed!" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file
|
||||
* @author danghai
|
||||
* @author [Piotr Idzik](https://github.com/vil02)
|
||||
* @brief This class specifies the basic operation on a stack as a linked list
|
||||
**/
|
||||
#ifndef DATA_STRUCTURES_STACK_HPP_
|
||||
#define DATA_STRUCTURES_STACK_HPP_
|
||||
|
||||
#include <stdexcept> /// for std::invalid_argument
|
||||
|
||||
#include "node.hpp" /// for Node
|
||||
|
||||
/** Definition of the stack class
|
||||
* \tparam value_type type of data nodes of the linked list in the stack should
|
||||
* contain
|
||||
*/
|
||||
template <class ValueType>
|
||||
class stack {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
|
||||
/** Show stack */
|
||||
void display() const {
|
||||
std::cout << "Top --> ";
|
||||
display_all(this->stackTop.get());
|
||||
std::cout << '\n';
|
||||
std::cout << "Size of stack: " << size << std::endl;
|
||||
}
|
||||
|
||||
std::vector<value_type> toVector() const {
|
||||
return push_all_to_vector(this->stackTop.get(), this->size);
|
||||
}
|
||||
|
||||
private:
|
||||
void ensureNotEmpty() const {
|
||||
if (isEmptyStack()) {
|
||||
throw std::invalid_argument("Stack is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/** Determine whether the stack is empty */
|
||||
bool isEmptyStack() const { return (stackTop == nullptr); }
|
||||
|
||||
/** Add new item to the stack */
|
||||
void push(const value_type& item) {
|
||||
auto newNode = std::make_shared<Node<value_type>>();
|
||||
newNode->data = item;
|
||||
newNode->next = stackTop;
|
||||
stackTop = newNode;
|
||||
size++;
|
||||
}
|
||||
|
||||
/** Return the top element of the stack */
|
||||
value_type top() const {
|
||||
ensureNotEmpty();
|
||||
return stackTop->data;
|
||||
}
|
||||
|
||||
/** Remove the top element of the stack */
|
||||
void pop() {
|
||||
ensureNotEmpty();
|
||||
stackTop = stackTop->next;
|
||||
size--;
|
||||
}
|
||||
|
||||
/** Clear stack */
|
||||
void clear() {
|
||||
stackTop = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Node<value_type>> stackTop =
|
||||
{}; /**< Pointer to the stack */
|
||||
std::size_t size = 0; ///< size of stack
|
||||
};
|
||||
|
||||
#endif // DATA_STRUCTURES_STACK_HPP_
|
||||
@@ -0,0 +1,179 @@
|
||||
#include <cassert> /// For std::assert
|
||||
#include <iostream> /// For std::cout
|
||||
#include <memory> /// For std::unique_ptr
|
||||
#include <stdexcept> /// For std::out_of_range
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief data_structures
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @brief Class representation of a stack
|
||||
* @tparam T The type of the elements in the stack
|
||||
*/
|
||||
template <typename T>
|
||||
class Stack {
|
||||
private:
|
||||
std::unique_ptr<T[]> stack; ///< Smart pointer to the stack array
|
||||
int stackSize; ///< Maximum size of the stack
|
||||
int stackIndex; ///< Index pointing to the top element of the stack
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new Stack object
|
||||
*
|
||||
* @param size Maximum size of the stack
|
||||
*/
|
||||
Stack(int size) : stack(new T[size]), stackSize(size), stackIndex(-1) {}
|
||||
|
||||
/**
|
||||
* @brief Checks if the stack is full
|
||||
*
|
||||
* @return true if the stack is full, false otherwise
|
||||
*/
|
||||
bool full() const { return stackIndex == stackSize - 1; }
|
||||
|
||||
/**
|
||||
* @brief Checks if the stack is empty
|
||||
* @return true if the stack is empty, false otherwise
|
||||
*/
|
||||
bool empty() const { return stackIndex == -1; }
|
||||
|
||||
/**
|
||||
* @brief Pushes an element onto the stack
|
||||
*
|
||||
* @param element Element to push onto the stack
|
||||
*/
|
||||
void push(T element) {
|
||||
if (full()) {
|
||||
throw std::out_of_range("Stack overflow");
|
||||
} else {
|
||||
stack[++stackIndex] = element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pops an element from the stack
|
||||
*
|
||||
* @return The popped element
|
||||
* @throws std::out_of_range if the stack is empty
|
||||
*/
|
||||
T pop() {
|
||||
if (empty()) {
|
||||
throw std::out_of_range("Stack underflow");
|
||||
}
|
||||
return stack[stackIndex--];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Displays all elements in the stack
|
||||
*/
|
||||
void show() const {
|
||||
for (int i = 0; i <= stackIndex; i++) {
|
||||
std::cout << stack[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Displays the topmost element of the stack
|
||||
*
|
||||
* @return The topmost element of the stack
|
||||
* @throws std::out_of_range if the stack is empty
|
||||
*/
|
||||
T topmost() const {
|
||||
if (empty()) {
|
||||
throw std::out_of_range("Stack underflow");
|
||||
}
|
||||
return stack[stackIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Displays the bottom element of the stack
|
||||
*
|
||||
* @return The bottom element of the stack
|
||||
* @throws std::out_of_range if the stack is empty
|
||||
*/
|
||||
T bottom() const {
|
||||
if (empty()) {
|
||||
throw std::out_of_range("Stack underflow");
|
||||
}
|
||||
return stack[0];
|
||||
}
|
||||
};
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::Stack<int> stack(5);
|
||||
|
||||
// Test empty and full operations
|
||||
assert(stack.empty());
|
||||
assert(!stack.full());
|
||||
|
||||
// Test pushing elements and checking topmost
|
||||
stack.push(10);
|
||||
assert(stack.topmost() == 10);
|
||||
|
||||
stack.push(20);
|
||||
assert(stack.topmost() == 20);
|
||||
|
||||
stack.push(30);
|
||||
stack.push(40);
|
||||
stack.push(50);
|
||||
assert(stack.full());
|
||||
|
||||
// Test stack overflow
|
||||
try {
|
||||
stack.push(60);
|
||||
} catch (const std::out_of_range& e) {
|
||||
assert(std::string(e.what()) == "Stack overflow");
|
||||
}
|
||||
|
||||
// Test popping elements
|
||||
assert(stack.pop() == 50);
|
||||
assert(stack.pop() == 40);
|
||||
assert(stack.pop() == 30);
|
||||
|
||||
// Check topmost and bottom elements
|
||||
assert(stack.topmost() == 20);
|
||||
assert(stack.bottom() == 10);
|
||||
|
||||
assert(stack.pop() == 20);
|
||||
assert(stack.pop() == 10);
|
||||
|
||||
assert(stack.empty());
|
||||
assert(!stack.full());
|
||||
|
||||
// Test stack underflow
|
||||
try {
|
||||
stack.pop();
|
||||
} catch (const std::out_of_range& e) {
|
||||
assert(std::string(e.what()) == "Stack underflow");
|
||||
}
|
||||
|
||||
try {
|
||||
stack.topmost();
|
||||
} catch (const std::out_of_range& e) {
|
||||
assert(std::string(e.what()) == "Stack underflow");
|
||||
}
|
||||
|
||||
try {
|
||||
stack.bottom();
|
||||
} catch (const std::out_of_range& e) {
|
||||
assert(std::string(e.what()) == "Stack underflow");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
std::cout << "All tests passed!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <iostream>
|
||||
|
||||
struct node {
|
||||
int val;
|
||||
node *next;
|
||||
};
|
||||
|
||||
node *top_var;
|
||||
|
||||
void push(int x) {
|
||||
node *n = new node;
|
||||
n->val = x;
|
||||
n->next = top_var;
|
||||
top_var = n;
|
||||
}
|
||||
|
||||
void pop() {
|
||||
if (top_var == nullptr) {
|
||||
std::cout << "\nUnderflow";
|
||||
} else {
|
||||
node *t = top_var;
|
||||
std::cout << "\n" << t->val << " deleted";
|
||||
top_var = top_var->next;
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
void show() {
|
||||
node *t = top_var;
|
||||
while (t != nullptr) {
|
||||
std::cout << t->val << "\n";
|
||||
t = t->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int ch = 0, x = 0;
|
||||
do {
|
||||
std::cout << "\n0. Exit or Ctrl+C";
|
||||
std::cout << "\n1. Push";
|
||||
std::cout << "\n2. Pop";
|
||||
std::cout << "\n3. Print";
|
||||
std::cout << "\nEnter Your Choice: ";
|
||||
std::cin >> ch;
|
||||
switch (ch) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
std::cout << "\nInsert : ";
|
||||
std::cin >> x;
|
||||
push(x);
|
||||
break;
|
||||
case 2:
|
||||
pop();
|
||||
break;
|
||||
case 3:
|
||||
show();
|
||||
break;
|
||||
default:
|
||||
std::cout << "Invalid option!\n";
|
||||
break;
|
||||
}
|
||||
} while (ch != 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @brief Stack Data Structure Using the Queue Data Structure
|
||||
* @details
|
||||
* Using 2 Queues inside the Stack class, we can easily implement Stack
|
||||
* data structure with heavy computation in push function.
|
||||
*
|
||||
* References used:
|
||||
* [StudyTonight](https://www.studytonight.com/data-structures/stack-using-queue)
|
||||
* @author [tushar2407](https://github.com/tushar2407)
|
||||
*/
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// for IO operations
|
||||
#include <queue> /// for queue data structure
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @namespace stack_using_queue
|
||||
* @brief Functions for the [Stack Using
|
||||
* Queue](https://www.studytonight.com/data-structures/stack-using-queue)
|
||||
* implementation
|
||||
*/
|
||||
namespace stack_using_queue {
|
||||
/**
|
||||
* @brief Stack Class implementation for basic methods of Stack Data Structure.
|
||||
*/
|
||||
struct Stack {
|
||||
std::queue<int64_t> main_q; ///< stores the current state of the stack
|
||||
std::queue<int64_t> auxiliary_q; ///< used to carry out intermediate
|
||||
///< operations to implement stack
|
||||
uint32_t current_size = 0; ///< stores the current size of the stack
|
||||
|
||||
/**
|
||||
* Returns the top most element of the stack
|
||||
* @returns top element of the queue
|
||||
*/
|
||||
int top() { return main_q.front(); }
|
||||
|
||||
/**
|
||||
* @brief Inserts an element to the top of the stack.
|
||||
* @param val the element that will be inserted into the stack
|
||||
* @returns void
|
||||
*/
|
||||
void push(int val) {
|
||||
auxiliary_q.push(val);
|
||||
while (!main_q.empty()) {
|
||||
auxiliary_q.push(main_q.front());
|
||||
main_q.pop();
|
||||
}
|
||||
swap(main_q, auxiliary_q);
|
||||
current_size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the topmost element from the stack
|
||||
* @returns void
|
||||
*/
|
||||
void pop() {
|
||||
if (main_q.empty()) {
|
||||
return;
|
||||
}
|
||||
main_q.pop();
|
||||
current_size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to return the current size of the stack
|
||||
* @returns current size of stack
|
||||
*/
|
||||
int size() { return current_size; }
|
||||
};
|
||||
} // namespace stack_using_queue
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::stack_using_queue::Stack s;
|
||||
s.push(1); /// insert an element into the stack
|
||||
s.push(2); /// insert an element into the stack
|
||||
s.push(3); /// insert an element into the stack
|
||||
|
||||
assert(s.size() == 3); /// size should be 3
|
||||
|
||||
assert(s.top() == 3); /// topmost element in the stack should be 3
|
||||
|
||||
s.pop(); /// remove the topmost element from the stack
|
||||
assert(s.top() == 2); /// topmost element in the stack should now be 2
|
||||
|
||||
s.pop(); /// remove the topmost element from the stack
|
||||
assert(s.top() == 1);
|
||||
|
||||
s.push(5); /// insert an element into the stack
|
||||
assert(s.top() == 5); /// topmost element in the stack should now be 5
|
||||
|
||||
s.pop(); /// remove the topmost element from the stack
|
||||
assert(s.top() == 1); /// topmost element in the stack should now be 1
|
||||
|
||||
assert(s.size() == 1); /// size should be 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* Creates a stack and pushed some value into it.
|
||||
* Through a series of push and pop functions on stack,
|
||||
* it demostrates the functionality of the custom stack
|
||||
* declared above.
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
3.4 Tom
|
||||
3.2 Kathy
|
||||
2.5 Hoang
|
||||
3.4 Tom
|
||||
3.8 Randy
|
||||
3.9 Kingston
|
||||
3.8 Mickey
|
||||
3.6 Peter
|
||||
3.5 Donald
|
||||
3.8 Cindy
|
||||
3.7 Dome
|
||||
3.9 Andy
|
||||
3.8 Hai
|
||||
3.9 Minnie
|
||||
2.7 Gilda
|
||||
3.9 Vinay
|
||||
3.4 Hiral
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for std::cout
|
||||
|
||||
#include "./queue.hpp"
|
||||
|
||||
template <typename T>
|
||||
void testConstructedQueueIsEmpty() {
|
||||
const queue<T> curQueue;
|
||||
assert(curQueue.isEmptyQueue());
|
||||
}
|
||||
|
||||
void testEnQueue() {
|
||||
queue<int> curQueue;
|
||||
curQueue.enQueue(10);
|
||||
assert(curQueue.toVector() == std::vector<int>({10}));
|
||||
curQueue.enQueue(20);
|
||||
assert(curQueue.toVector() == std::vector<int>({10, 20}));
|
||||
curQueue.enQueue(30);
|
||||
curQueue.enQueue(40);
|
||||
assert(curQueue.toVector() == std::vector<int>({10, 20, 30, 40}));
|
||||
}
|
||||
|
||||
void testDeQueue() {
|
||||
queue<int> curQueue;
|
||||
curQueue.enQueue(10);
|
||||
curQueue.enQueue(20);
|
||||
curQueue.enQueue(30);
|
||||
|
||||
curQueue.deQueue();
|
||||
assert(curQueue.toVector() == std::vector<int>({20, 30}));
|
||||
|
||||
curQueue.deQueue();
|
||||
assert(curQueue.toVector() == std::vector<int>({30}));
|
||||
|
||||
curQueue.deQueue();
|
||||
assert(curQueue.isEmptyQueue());
|
||||
}
|
||||
|
||||
void testFront() {
|
||||
queue<int> curQueue;
|
||||
curQueue.enQueue(10);
|
||||
assert(curQueue.front() == 10);
|
||||
curQueue.enQueue(20);
|
||||
assert(curQueue.front() == 10);
|
||||
}
|
||||
|
||||
void testQueueAfterClearIsEmpty() {
|
||||
queue<int> curQueue;
|
||||
curQueue.enQueue(10);
|
||||
curQueue.enQueue(20);
|
||||
curQueue.enQueue(30);
|
||||
curQueue.clear();
|
||||
assert(curQueue.isEmptyQueue());
|
||||
}
|
||||
|
||||
void testFrontThrowsAnInvalidArgumentWhenQueueEmpty() {
|
||||
const queue<int> curQueue;
|
||||
bool wasException = false;
|
||||
try {
|
||||
curQueue.front();
|
||||
} catch (const std::invalid_argument&) {
|
||||
wasException = true;
|
||||
}
|
||||
assert(wasException);
|
||||
}
|
||||
|
||||
void testDeQueueThrowsAnInvalidArgumentWhenQueueEmpty() {
|
||||
queue<int> curQueue;
|
||||
bool wasException = false;
|
||||
try {
|
||||
curQueue.deQueue();
|
||||
} catch (const std::invalid_argument&) {
|
||||
wasException = true;
|
||||
}
|
||||
assert(wasException);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testConstructedQueueIsEmpty<int>();
|
||||
testConstructedQueueIsEmpty<double>();
|
||||
testConstructedQueueIsEmpty<std::vector<long double>>();
|
||||
|
||||
testEnQueue();
|
||||
testDeQueue();
|
||||
|
||||
testQueueAfterClearIsEmpty();
|
||||
|
||||
testFrontThrowsAnInvalidArgumentWhenQueueEmpty();
|
||||
testDeQueueThrowsAnInvalidArgumentWhenQueueEmpty();
|
||||
|
||||
std::cout << "All tests pass!\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for std::cout
|
||||
#include <stdexcept> /// std::invalid_argument
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
#include "./stack.hpp"
|
||||
|
||||
template <typename T>
|
||||
void testConstructedStackIsEmpty() {
|
||||
const stack<T> curStack;
|
||||
assert(curStack.isEmptyStack());
|
||||
}
|
||||
|
||||
void testPush() {
|
||||
using valueType = int;
|
||||
stack<valueType> curStack;
|
||||
curStack.push(10);
|
||||
curStack.push(20);
|
||||
curStack.push(30);
|
||||
curStack.push(40);
|
||||
const auto expectedData = std::vector<valueType>({40, 30, 20, 10});
|
||||
assert(curStack.toVector() == expectedData);
|
||||
}
|
||||
|
||||
void testTop() {
|
||||
using valueType = unsigned;
|
||||
stack<valueType> curStack;
|
||||
curStack.push(1);
|
||||
curStack.push(2);
|
||||
curStack.push(3);
|
||||
curStack.push(4);
|
||||
assert(curStack.top() == static_cast<valueType>(4));
|
||||
}
|
||||
|
||||
void testPop() {
|
||||
using valueType = int;
|
||||
stack<valueType> curStack;
|
||||
curStack.push(100);
|
||||
curStack.push(200);
|
||||
curStack.push(300);
|
||||
|
||||
assert(curStack.top() == static_cast<valueType>(300));
|
||||
curStack.pop();
|
||||
assert(curStack.top() == static_cast<valueType>(200));
|
||||
curStack.pop();
|
||||
assert(curStack.top() == static_cast<valueType>(100));
|
||||
curStack.pop();
|
||||
assert(curStack.isEmptyStack());
|
||||
}
|
||||
|
||||
void testClear() {
|
||||
stack<int> curStack;
|
||||
curStack.push(1000);
|
||||
curStack.push(2000);
|
||||
curStack.clear();
|
||||
assert(curStack.isEmptyStack());
|
||||
}
|
||||
|
||||
void testCopyOfStackHasSameData() {
|
||||
stack<int> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(200);
|
||||
stackA.push(3000);
|
||||
const auto stackB(stackA);
|
||||
assert(stackA.toVector() == stackB.toVector());
|
||||
}
|
||||
|
||||
void testPushingToCopyDoesNotChangeOriginal() {
|
||||
using valueType = int;
|
||||
stack<valueType> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(20);
|
||||
stackA.push(30);
|
||||
auto stackB(stackA);
|
||||
stackB.push(40);
|
||||
|
||||
const auto expectedDataA = std::vector<valueType>({30, 20, 10});
|
||||
const auto expectedDataB = std::vector<valueType>({40, 30, 20, 10});
|
||||
|
||||
assert(stackA.toVector() == expectedDataA);
|
||||
assert(stackB.toVector() == expectedDataB);
|
||||
}
|
||||
|
||||
void testPoppingFromCopyDoesNotChangeOriginal() {
|
||||
using valueType = int;
|
||||
stack<valueType> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(20);
|
||||
stackA.push(30);
|
||||
auto stackB(stackA);
|
||||
stackB.pop();
|
||||
|
||||
const auto expectedDataA = std::vector<valueType>({30, 20, 10});
|
||||
const auto expectedDataB = std::vector<valueType>({20, 10});
|
||||
|
||||
assert(stackA.toVector() == expectedDataA);
|
||||
assert(stackB.toVector() == expectedDataB);
|
||||
}
|
||||
|
||||
void testPushingToOrginalDoesNotChangeCopy() {
|
||||
using valueType = int;
|
||||
stack<valueType> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(20);
|
||||
stackA.push(30);
|
||||
const auto stackB(stackA);
|
||||
stackA.push(40);
|
||||
|
||||
const auto expectedDataA = std::vector<valueType>({40, 30, 20, 10});
|
||||
const auto expectedDataB = std::vector<valueType>({30, 20, 10});
|
||||
|
||||
assert(stackA.toVector() == expectedDataA);
|
||||
assert(stackB.toVector() == expectedDataB);
|
||||
}
|
||||
|
||||
void testPoppingFromOrginalDoesNotChangeCopy() {
|
||||
using valueType = int;
|
||||
stack<valueType> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(20);
|
||||
stackA.push(30);
|
||||
const auto stackB(stackA);
|
||||
stackA.pop();
|
||||
|
||||
const auto expectedDataA = std::vector<valueType>({20, 10});
|
||||
const auto expectedDataB = std::vector<valueType>({30, 20, 10});
|
||||
|
||||
assert(stackA.toVector() == expectedDataA);
|
||||
assert(stackB.toVector() == expectedDataB);
|
||||
}
|
||||
|
||||
void testAssign() {
|
||||
using valueType = int;
|
||||
stack<valueType> stackA;
|
||||
stackA.push(10);
|
||||
stackA.push(20);
|
||||
stackA.push(30);
|
||||
stack<valueType> stackB = stackA;
|
||||
stackA.pop();
|
||||
stackB.push(40);
|
||||
|
||||
const auto expectedDataA = std::vector<valueType>({20, 10});
|
||||
const auto expectedDataB = std::vector<valueType>({40, 30, 20, 10});
|
||||
|
||||
assert(stackA.toVector() == expectedDataA);
|
||||
assert(stackB.toVector() == expectedDataB);
|
||||
|
||||
stackB = stackA;
|
||||
stackA.pop();
|
||||
stackB.push(5);
|
||||
stackB.push(6);
|
||||
|
||||
const auto otherExpectedDataA = std::vector<valueType>({10});
|
||||
const auto otherExpectedDataB = std::vector<valueType>({6, 5, 20, 10});
|
||||
|
||||
assert(stackA.toVector() == otherExpectedDataA);
|
||||
assert(stackB.toVector() == otherExpectedDataB);
|
||||
}
|
||||
|
||||
void testTopThrowsAnInvalidArgumentWhenStackEmpty() {
|
||||
const stack<long double> curStack;
|
||||
bool wasException = false;
|
||||
try {
|
||||
curStack.top();
|
||||
} catch (const std::invalid_argument&) {
|
||||
wasException = true;
|
||||
}
|
||||
assert(wasException);
|
||||
}
|
||||
|
||||
void testPopThrowsAnInvalidArgumentWhenStackEmpty() {
|
||||
stack<bool> curStack;
|
||||
bool wasException = false;
|
||||
try {
|
||||
curStack.pop();
|
||||
} catch (const std::invalid_argument&) {
|
||||
wasException = true;
|
||||
}
|
||||
assert(wasException);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testConstructedStackIsEmpty<int>();
|
||||
testConstructedStackIsEmpty<char>();
|
||||
|
||||
testPush();
|
||||
testPop();
|
||||
testClear();
|
||||
|
||||
testCopyOfStackHasSameData();
|
||||
testPushingToCopyDoesNotChangeOriginal();
|
||||
testPoppingFromCopyDoesNotChangeOriginal();
|
||||
testPushingToOrginalDoesNotChangeCopy();
|
||||
testPoppingFromOrginalDoesNotChangeCopy();
|
||||
|
||||
testAssign();
|
||||
|
||||
testTopThrowsAnInvalidArgumentWhenStackEmpty();
|
||||
testPopThrowsAnInvalidArgumentWhenStackEmpty();
|
||||
|
||||
std::cout << "All tests pass!\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This program reads a data file consisting of students' GPAs
|
||||
* followed by their names. The program then prints the highest
|
||||
* GPA and the names of the students with the highest GPA.
|
||||
* It uses stack to store the names of the students
|
||||
* Run:
|
||||
* make all
|
||||
* ./main student.txt
|
||||
************************************************************
|
||||
* */
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "./stack.hpp"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
double GPA = NAN;
|
||||
double highestGPA = NAN;
|
||||
std::string name;
|
||||
|
||||
assert(argc == 2);
|
||||
std::ifstream infile;
|
||||
stack<std::string> stk;
|
||||
|
||||
infile.open(argv[1]);
|
||||
std::cout << std::fixed << std::showpoint;
|
||||
std::cout << std::setprecision(2);
|
||||
infile >> GPA >> name;
|
||||
highestGPA = GPA;
|
||||
|
||||
while (infile) {
|
||||
if (GPA > highestGPA) {
|
||||
stk.clear();
|
||||
stk.push(name);
|
||||
highestGPA = GPA;
|
||||
} else if (GPA == highestGPA) {
|
||||
stk.push(name);
|
||||
}
|
||||
infile >> GPA >> name;
|
||||
}
|
||||
std::cout << "Highest GPA: " << highestGPA << std::endl;
|
||||
std::cout << "Students the highest GPA are: " << std::endl;
|
||||
while (!stk.isEmptyStack()) {
|
||||
std::cout << stk.top() << std::endl;
|
||||
stk.pop();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A balanced binary search tree (BST) on the basis of binary search tree
|
||||
* and heap: the [Treap](https://en.wikipedia.org/wiki/Treap) algorithm
|
||||
* implementation
|
||||
*
|
||||
* @details
|
||||
* Implementation of the treap data structre
|
||||
*
|
||||
* Support operations including insert, erase, and query (the rank of specified
|
||||
* element or the element ranked x) as the same as BST
|
||||
*
|
||||
* But these operations take O(log N) time, since treap keeps property of heap
|
||||
* using rotate operation, and the desired depth of the tree is O(log N).
|
||||
* There's very little chance that it will degenerate into a chain like BST
|
||||
*
|
||||
* @author [Kairao ZHENG](https://github.com/fgmn)
|
||||
*/
|
||||
|
||||
#include <array> /// For array
|
||||
#include <cassert> /// For assert
|
||||
#include <cstdint>
|
||||
#include <iostream> /// For IO operations
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Data Structures
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @namespace
|
||||
* @brief Functions for the [Treap](https://en.wikipedia.org/wiki/Treap)
|
||||
* algorithm implementation
|
||||
*/
|
||||
namespace treap {
|
||||
const int maxNode = 1e5 + 5; ///< maximum number of nodes
|
||||
/**
|
||||
* @brief Struct representation of the treap
|
||||
*/
|
||||
struct Treap {
|
||||
int root = 0; ///< root of the treap
|
||||
int treapCnt = 0; ///< Total number of current nodes in the treap
|
||||
std::array<int, maxNode> key = {}; ///< Node identifier
|
||||
std::array<int, maxNode> priority = {}; ///< Random priority
|
||||
std::array<std::array<int, 2>, maxNode> childs = {
|
||||
{}}; ///< [i][0] represents the
|
||||
///< left child of node i, and
|
||||
///[i][1] represents the right
|
||||
std::array<int, maxNode> cnt =
|
||||
{}; ///< Maintains the subtree size for ranking query
|
||||
std::array<int, maxNode> size = {}; ///< The number of copies per node
|
||||
/**
|
||||
* @brief Initialization
|
||||
*/
|
||||
Treap() : treapCnt(1) {
|
||||
priority[0] = INT32_MAX;
|
||||
size[0] = 0;
|
||||
}
|
||||
/**
|
||||
* @brief Update the subtree size of the node
|
||||
* @param x The node to update
|
||||
*/
|
||||
void update(int x) {
|
||||
size[x] = size[childs[x][0]] + cnt[x] + size[childs[x][1]];
|
||||
}
|
||||
/**
|
||||
* @brief Rotate without breaking the property of BST
|
||||
* @param x The node to rotate
|
||||
* @param t 0 represent left hand, while 1 right hand
|
||||
*/
|
||||
void rotate(int &x, int t) {
|
||||
int y = childs[x][t];
|
||||
childs[x][t] = childs[y][1 - t];
|
||||
childs[y][1 - t] = x;
|
||||
// The rotation will only change itself and its son nodes
|
||||
update(x);
|
||||
update(y);
|
||||
x = y;
|
||||
}
|
||||
/**
|
||||
* @brief Insert a value into the specified subtree (internal method)
|
||||
* @param x Insert into the subtree of node x (Usually x=root)
|
||||
* @param k Key to insert
|
||||
*/
|
||||
void _insert(int &x, int k) {
|
||||
if (x) {
|
||||
if (key[x] == k) {
|
||||
cnt[x]++;
|
||||
} // If the node already exists, the number of copies is ++
|
||||
else {
|
||||
int t = (key[x] < k); // Insert according to BST properties
|
||||
_insert(childs[x][t], k);
|
||||
// After insertion, the heap properties are retained by rotation
|
||||
if (priority[childs[x][t]] < priority[x]) {
|
||||
rotate(x, t);
|
||||
}
|
||||
}
|
||||
} else { // Create a new node
|
||||
x = treapCnt++;
|
||||
key[x] = k;
|
||||
cnt[x] = 1;
|
||||
priority[x] = rand(); // Random priority
|
||||
childs[x][0] = childs[x][1] = 0;
|
||||
}
|
||||
update(x);
|
||||
}
|
||||
/**
|
||||
* @brief Erase a value from the specified subtree (internal method)
|
||||
* @param x Erase from the subtree of node x (Usually x=root)
|
||||
* @param k Key to erase
|
||||
*/
|
||||
void _erase(int &x, int k) {
|
||||
if (key[x] == k) {
|
||||
if (cnt[x] > 1) {
|
||||
cnt[x]--;
|
||||
} // If the node has more than one copy, the number of copies --
|
||||
else {
|
||||
if (childs[x][0] == 0 && childs[x][1] == 0) {
|
||||
x = 0;
|
||||
return;
|
||||
} // If there are no children, delete and return
|
||||
// Otherwise, we need to rotate the sons and delete them
|
||||
// recursively
|
||||
int t = (priority[childs[x][0]] > priority[childs[x][1]]);
|
||||
rotate(x, t);
|
||||
_erase(x, k);
|
||||
}
|
||||
} else { // Find the target value based on BST properties
|
||||
_erase(childs[x][key[x] < k], k);
|
||||
}
|
||||
update(x);
|
||||
}
|
||||
/**
|
||||
* @brief Find the KTH largest value (internal method)
|
||||
* @param x Query the subtree of node x (Usually x=root)
|
||||
* @param k The queried rank
|
||||
* @return The element ranked number k
|
||||
*/
|
||||
int _get_k_th(int &x, int k) {
|
||||
if (k <= size[childs[x][0]]) {
|
||||
return _get_k_th(childs[x][0], k);
|
||||
}
|
||||
k -= size[childs[x][0]] + cnt[x];
|
||||
if (k <= 0) {
|
||||
return key[x];
|
||||
}
|
||||
return _get_k_th(childs[x][1], k);
|
||||
}
|
||||
/**
|
||||
* @brief Query the rank of specified element (internal method)
|
||||
* @param x Query the subtree of node x (Usually x=root)
|
||||
* @param k The queried element
|
||||
* @return The rank of element k
|
||||
*/
|
||||
int _get_rank(int x, int k) {
|
||||
if (!x) {
|
||||
return 0;
|
||||
}
|
||||
if (k == key[x]) {
|
||||
return size[childs[x][0]] + 1;
|
||||
} else if (k < key[x]) {
|
||||
return _get_rank(childs[x][0], k);
|
||||
} else {
|
||||
return size[childs[x][0]] + cnt[x] + _get_rank(childs[x][1], k);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Get the predecessor node of element k
|
||||
* @param k The queried element
|
||||
* @return The predecessor
|
||||
*/
|
||||
int get_predecessor(int k) {
|
||||
int x = root, pre = -1;
|
||||
while (x) {
|
||||
if (key[x] < k) {
|
||||
pre = key[x], x = childs[x][1];
|
||||
} else {
|
||||
x = childs[x][0];
|
||||
}
|
||||
}
|
||||
return pre;
|
||||
}
|
||||
/**
|
||||
* @brief Get the successor node of element k
|
||||
* @param k The queried element
|
||||
* @return The successor
|
||||
*/
|
||||
int get_next(int k) {
|
||||
int x = root, next = -1;
|
||||
while (x) {
|
||||
if (key[x] > k) {
|
||||
next = key[x], x = childs[x][0];
|
||||
} else {
|
||||
x = childs[x][1];
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
/**
|
||||
* @brief Insert element (External method)
|
||||
* @param k Key to insert
|
||||
*/
|
||||
void insert(int k) { _insert(root, k); }
|
||||
/**
|
||||
* @brief Erase element (External method)
|
||||
* @param k Key to erase
|
||||
*/
|
||||
void erase(int k) { _erase(root, k); }
|
||||
/**
|
||||
* @brief Get the KTH largest value (External method)
|
||||
* @param k The queried rank
|
||||
* @return The element ranked number x
|
||||
*/
|
||||
int get_k_th(int k) { return _get_k_th(root, k); }
|
||||
/**
|
||||
* @brief Get the rank of specified element (External method)
|
||||
* @param k The queried element
|
||||
* @return The rank of element k
|
||||
*/
|
||||
int get_rank(int k) { return _get_rank(root, k); }
|
||||
};
|
||||
} // namespace treap
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::treap::Treap mTreap; ///< Treap object instance
|
||||
|
||||
mTreap.insert(1);
|
||||
mTreap.insert(2);
|
||||
mTreap.insert(3);
|
||||
assert(mTreap.get_k_th(2) == 2);
|
||||
mTreap.insert(4);
|
||||
mTreap.insert(5);
|
||||
mTreap.insert(6);
|
||||
assert(mTreap.get_next(4) == 5);
|
||||
mTreap.insert(7);
|
||||
assert(mTreap.get_predecessor(7) == 6);
|
||||
mTreap.erase(4);
|
||||
assert(mTreap.get_k_th(4) == 5);
|
||||
assert(mTreap.get_rank(5) == 4);
|
||||
mTreap.insert(10);
|
||||
assert(mTreap.get_rank(10) == 7);
|
||||
assert(mTreap.get_predecessor(10) == 7);
|
||||
|
||||
std::cout << "All tests have successfully passed!\n";
|
||||
}
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
using namespace std;
|
||||
|
||||
struct node {
|
||||
int val;
|
||||
node *left;
|
||||
node *right;
|
||||
};
|
||||
|
||||
void CreateTree(node *curr, node *n, int x, char pos) {
|
||||
if (n != NULL) {
|
||||
char ch;
|
||||
cout << "\nLeft or Right of " << n->val << " : ";
|
||||
cin >> ch;
|
||||
if (ch == 'l')
|
||||
CreateTree(n, n->left, x, ch);
|
||||
else if (ch == 'r')
|
||||
CreateTree(n, n->right, x, ch);
|
||||
} else {
|
||||
node *t = new node;
|
||||
t->val = x;
|
||||
t->left = NULL;
|
||||
t->right = NULL;
|
||||
if (pos == 'l') {
|
||||
curr->left = t;
|
||||
} else if (pos == 'r') {
|
||||
curr->right = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BFT(node *n) {
|
||||
list<node *> queue;
|
||||
|
||||
queue.push_back(n);
|
||||
|
||||
while (!queue.empty()) {
|
||||
n = queue.front();
|
||||
cout << n->val << " ";
|
||||
queue.pop_front();
|
||||
|
||||
if (n->left != NULL)
|
||||
queue.push_back(n->left);
|
||||
if (n->right != NULL)
|
||||
queue.push_back(n->right);
|
||||
}
|
||||
}
|
||||
|
||||
void Pre(node *n) {
|
||||
if (n != NULL) {
|
||||
cout << n->val << " ";
|
||||
Pre(n->left);
|
||||
Pre(n->right);
|
||||
}
|
||||
}
|
||||
|
||||
void In(node *n) {
|
||||
if (n != NULL) {
|
||||
In(n->left);
|
||||
cout << n->val << " ";
|
||||
In(n->right);
|
||||
}
|
||||
}
|
||||
|
||||
void Post(node *n) {
|
||||
if (n != NULL) {
|
||||
Post(n->left);
|
||||
Post(n->right);
|
||||
cout << n->val << " ";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int value;
|
||||
int ch;
|
||||
node *root = new node;
|
||||
cout << "\nEnter the value of root node :";
|
||||
cin >> value;
|
||||
root->val = value;
|
||||
root->left = NULL;
|
||||
root->right = NULL;
|
||||
do {
|
||||
cout << "\n1. Insert";
|
||||
cout << "\n2. Breadth First";
|
||||
cout << "\n3. Preorder Depth First";
|
||||
cout << "\n4. Inorder Depth First";
|
||||
cout << "\n5. Postorder Depth First";
|
||||
|
||||
cout << "\nEnter Your Choice : ";
|
||||
cin >> ch;
|
||||
switch (ch) {
|
||||
case 1:
|
||||
int x;
|
||||
char pos;
|
||||
cout << "\nEnter the value to be Inserted : ";
|
||||
cin >> x;
|
||||
cout << "\nLeft or Right of Root : ";
|
||||
cin >> pos;
|
||||
if (pos == 'l')
|
||||
CreateTree(root, root->left, x, pos);
|
||||
else if (pos == 'r')
|
||||
CreateTree(root, root->right, x, pos);
|
||||
break;
|
||||
case 2:
|
||||
BFT(root);
|
||||
break;
|
||||
case 3:
|
||||
Pre(root);
|
||||
break;
|
||||
case 4:
|
||||
In(root);
|
||||
break;
|
||||
case 5:
|
||||
Post(root);
|
||||
break;
|
||||
}
|
||||
} while (ch != 0);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @author Anmol3299
|
||||
* \brief A basic implementation of trie class to store only lower-case strings.
|
||||
*/
|
||||
#include <iostream> // for io operations
|
||||
#include <memory> // for std::shared_ptr<>
|
||||
#include <string> // for std::string class
|
||||
|
||||
/**
|
||||
* A basic implementation of trie class to store only lower-case strings.
|
||||
* You can extend the implementation to all the ASCII characters by changing the
|
||||
* value of @ ALPHABETS to 128.
|
||||
*/
|
||||
class Trie {
|
||||
private:
|
||||
static constexpr size_t ALPHABETS = 26;
|
||||
|
||||
/**
|
||||
* Structure of trie node.
|
||||
* This struct doesn't need a constructor as we are initializing using
|
||||
* intializer list which is more efficient than if we had done so with
|
||||
* constructor.
|
||||
*/
|
||||
struct TrieNode {
|
||||
// An array of pointers of size 26 which tells if a character of word is
|
||||
// present or not.
|
||||
std::shared_ptr<TrieNode> character[ALPHABETS]{nullptr};
|
||||
|
||||
bool isEndOfWord{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to check if a node has some children which can form words.
|
||||
* @param node whose character array of pointers need to be checked for
|
||||
* children.
|
||||
* @return `true` if a child is found
|
||||
* @return `false` if a child is not found
|
||||
*/
|
||||
inline static bool hasChildren(std::shared_ptr<TrieNode> node) {
|
||||
for (size_t i = 0; i < ALPHABETS; i++) {
|
||||
if (node->character[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A recursive helper function to remove a word from the trie. First, it
|
||||
* recursively traverses to the location of last character of word in the
|
||||
* trie. However, if the word is not found, the function returns a runtime
|
||||
* error. Upon successfully reaching the last character of word in trie, if
|
||||
* sets the isEndOfWord to false and deletes the node if and only if it has
|
||||
* no children, else it returns the current node.
|
||||
* @param word is the string which needs to be removed from trie.
|
||||
* @param curr is the current node we are at.
|
||||
* @param index is the index of the @word we are at.
|
||||
* @return if current node has childern, it returns @ curr, else it returns
|
||||
* nullptr.
|
||||
* @throw a runtime error in case @ word is not found in the trie.
|
||||
*/
|
||||
std::shared_ptr<TrieNode> removeWordHelper(const std::string& word,
|
||||
std::shared_ptr<TrieNode> curr,
|
||||
size_t index) {
|
||||
if (word.size() == index) {
|
||||
if (curr->isEndOfWord) {
|
||||
curr->isEndOfWord = false;
|
||||
}
|
||||
if (hasChildren(curr)) {
|
||||
return curr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t idx = word[index] - 'a';
|
||||
|
||||
// Throw a runtime error in case the user enters a word which is not
|
||||
// present in the trie.
|
||||
if (!curr->character[idx]) {
|
||||
throw std::runtime_error(std::move(std::string("Word not found.")));
|
||||
}
|
||||
|
||||
curr->character[idx] =
|
||||
removeWordHelper(word, curr->character[idx], index + 1);
|
||||
|
||||
// This if condition checks if the node has some childern.
|
||||
// The 1st if check, i.e. (curr->character[idx]) is checked specifically
|
||||
// because if the older string is a prefix of some other string, then,
|
||||
// there would be no need to check all 26 characters. Example- str1 =
|
||||
// abbey, str2 = abbex and we want to delete string "abbey", then in
|
||||
// this case, there would be no need to check all characters for the
|
||||
// chars a,b,b.
|
||||
if (curr->character[idx] || hasChildren(curr)) {
|
||||
return curr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/// constructor to initialise the root of the trie.
|
||||
Trie() : m_root(std::make_shared<TrieNode>()) {}
|
||||
|
||||
/**
|
||||
* Insert a word into the trie.
|
||||
* @param word which needs to be inserted into the string.
|
||||
*/
|
||||
void insert(const std::string& word) {
|
||||
auto curr = m_root;
|
||||
for (char ch : word) {
|
||||
size_t index = ch - 'a';
|
||||
|
||||
// if a node for current word is not already present in trie, create
|
||||
// a new node for it.
|
||||
if (!curr->character[index]) {
|
||||
curr->character[index] = std::make_shared<TrieNode>();
|
||||
}
|
||||
|
||||
curr = curr->character[index];
|
||||
}
|
||||
curr->isEndOfWord = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search if a word is present in trie or not.
|
||||
* @param word which is needed to be searched in the trie.
|
||||
* @return True if the word is found in trie and isEndOfWord is set to true.
|
||||
* @return False if word is not found in trie or isEndOfWord is set to
|
||||
* false.
|
||||
*/
|
||||
bool search(const std::string& word) {
|
||||
auto curr = m_root;
|
||||
for (char ch : word) {
|
||||
size_t index = ch - 'a';
|
||||
|
||||
// if any node for a character is not found, then return that the
|
||||
// word cannot be formed.
|
||||
if (!curr->character[index]) {
|
||||
return false;
|
||||
}
|
||||
curr = curr->character[index];
|
||||
}
|
||||
return curr->isEndOfWord;
|
||||
}
|
||||
|
||||
// Function to remove the word which calls the helper function.
|
||||
void removeWord(const std::string& word) {
|
||||
m_root = removeWordHelper(word, m_root, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
// data member to store the root of the trie.
|
||||
std::shared_ptr<TrieNode> m_root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
int main() {
|
||||
Trie trie;
|
||||
trie.insert("hel");
|
||||
trie.insert("hello");
|
||||
trie.removeWord("hel");
|
||||
std::cout << trie.search("hello") << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* @file
|
||||
* @author [@Arctic2333](https://github.com/Arctic2333)
|
||||
* @author [Krishna Vedala](https://github.com/kvedala)
|
||||
* @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data
|
||||
* structure for English alphabets in small characters.
|
||||
* @note the function ::data_structure::trie::deleteString might be erroneous
|
||||
* @see trie_modern.cpp
|
||||
*/
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/** \namespace data_structures
|
||||
* \brief Data-structure algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
/**
|
||||
* @brief [Trie](https://en.wikipedia.org/wiki/Trie) implementation for
|
||||
* small-case English alphabets `a-z`
|
||||
*/
|
||||
class trie {
|
||||
private:
|
||||
static constexpr uint8_t NUM_CHARS = 26; ///< Number of alphabets
|
||||
/** @brief Recursive tree nodes as an array of shared-pointers */
|
||||
std::array<std::shared_ptr<trie>, NUM_CHARS << 1> arr;
|
||||
bool isEndofWord = false; ///< identifier if a node is terminal node
|
||||
|
||||
/**
|
||||
* @brief Convert a character to integer for indexing
|
||||
*
|
||||
* @param ch character to index
|
||||
* @return unsigned integer index
|
||||
*/
|
||||
uint8_t char_to_int(const char& ch) const {
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
return ch - 'A';
|
||||
} else if (ch >= 'a' && ch <= 'z') {
|
||||
return ch - 'a' + NUM_CHARS;
|
||||
}
|
||||
|
||||
std::cerr << "Invalid character present. Exiting...";
|
||||
std::exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** search a string exists inside a given root trie
|
||||
* @param str string to search for
|
||||
* @param index start index to search from
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool search(const std::shared_ptr<trie>& root, const std::string& str,
|
||||
int index) {
|
||||
if (index == str.length()) {
|
||||
if (!root->isEndofWord) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int j = char_to_int(str[index]);
|
||||
if (!root->arr[j]) {
|
||||
return false;
|
||||
}
|
||||
return search(root->arr[j], str, index + 1);
|
||||
}
|
||||
|
||||
public:
|
||||
trie() = default; ///< Class default constructor
|
||||
|
||||
/** insert string into the trie
|
||||
* @param str String to insert in the tree
|
||||
*/
|
||||
void insert(const std::string& str) {
|
||||
std::shared_ptr<trie> root(nullptr);
|
||||
|
||||
for (const char& ch : str) {
|
||||
int j = char_to_int(ch);
|
||||
if (root) {
|
||||
if (root->arr[j]) {
|
||||
root = root->arr[j];
|
||||
} else {
|
||||
std::shared_ptr<trie> temp(new trie());
|
||||
root->arr[j] = temp;
|
||||
root = temp;
|
||||
}
|
||||
} else if (arr[j]) {
|
||||
root = arr[j];
|
||||
} else {
|
||||
std::shared_ptr<trie> temp(new trie());
|
||||
arr[j] = temp;
|
||||
root = temp;
|
||||
}
|
||||
}
|
||||
root->isEndofWord = true;
|
||||
}
|
||||
|
||||
/** search a string exists inside the trie
|
||||
* @param str string to search for
|
||||
* @param index start index to search from
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool search(const std::string& str, int index) {
|
||||
if (index == str.length()) {
|
||||
if (!isEndofWord) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int j = char_to_int(str[index]);
|
||||
if (!arr[j]) {
|
||||
return false;
|
||||
}
|
||||
return search(arr[j], str, index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the string if it is not a prefix of any other
|
||||
* string, if it is then just sets the ::data_structure::trie::isEndofWord
|
||||
* to false, else removes the given string
|
||||
* @note the function ::data_structure::trie::deleteString might be
|
||||
* erroneous
|
||||
* @todo review the function ::data_structure::trie::deleteString and the
|
||||
* commented lines
|
||||
* @param str string to remove
|
||||
* @param index index to remove from
|
||||
* @returns `true` if successful
|
||||
* @returns `false` if unsuccessful
|
||||
*/
|
||||
bool deleteString(const std::string& str, int index) {
|
||||
if (index == str.length()) {
|
||||
if (!isEndofWord) {
|
||||
return false;
|
||||
}
|
||||
isEndofWord = false;
|
||||
// following lines - possible source of error?
|
||||
// for (int i = 0; i < NUM_CHARS; i++)
|
||||
// if (!arr[i])
|
||||
// return false;
|
||||
return true;
|
||||
}
|
||||
int j = char_to_int(str[index]);
|
||||
if (!arr[j]) {
|
||||
return false;
|
||||
}
|
||||
bool var = deleteString(str, index + 1);
|
||||
if (var) {
|
||||
arr[j].reset();
|
||||
if (isEndofWord) {
|
||||
return false;
|
||||
} else {
|
||||
int i = 0;
|
||||
for (i = 0; i < NUM_CHARS; i++) {
|
||||
if (arr[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* should not return here */
|
||||
std::cout << __func__ << ":" << __LINE__
|
||||
<< "Should not reach this line\n";
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Testing function
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::trie root;
|
||||
root.insert("Hello");
|
||||
root.insert("World");
|
||||
|
||||
assert(!root.search("hello", 0));
|
||||
std::cout << "hello - " << root.search("hello", 0) << "\n";
|
||||
|
||||
assert(root.search("Hello", 0));
|
||||
std::cout << "Hello - " << root.search("Hello", 0) << "\n";
|
||||
|
||||
assert(!root.search("Word", 0));
|
||||
std::cout << "Word - " << root.search("Word", 0) << "\n";
|
||||
|
||||
assert(root.search("World", 0));
|
||||
std::cout << "World - " << root.search("World", 0) << "\n";
|
||||
|
||||
// Following lines of code give erroneous output
|
||||
// root.deleteString("hello", 0);
|
||||
// assert(!root.search("hello", 0));
|
||||
// std::cout << "hello - " << root.search("world", 0) << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @return 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* @file
|
||||
* @author [Venkata Bharath](https://github.com/bharath000)
|
||||
* @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data
|
||||
* structure using HashMap for different characters and method for predicting
|
||||
* words based on prefix.
|
||||
* @details The Trie data structure is implemented using unordered map to use
|
||||
* memory optimally, predict_words method is developed to recommend words based
|
||||
* on a given prefix along with other methods insert, delete, search, startwith
|
||||
* in trie.
|
||||
* @see trie_modern.cpp for difference
|
||||
*/
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for IO operations
|
||||
#include <memory> /// for std::shared_ptr
|
||||
#include <stack> /// for std::stack
|
||||
#include <unordered_map> /// for std::unordered_map
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
|
||||
/**
|
||||
* @namespace trie_using_hashmap
|
||||
* @brief Functions for [Trie](https://en.wikipedia.org/wiki/Trie) data
|
||||
* structure using hashmap implementation
|
||||
*/
|
||||
namespace trie_using_hashmap {
|
||||
|
||||
/**
|
||||
* @brief Trie class, implementation of trie using hashmap in each trie node
|
||||
* for all the characters of char16_t(UTF-16)type with methods to insert,
|
||||
* delete, search, start with and to recommend words based on a given
|
||||
* prefix.
|
||||
*/
|
||||
class Trie {
|
||||
private:
|
||||
/**
|
||||
* @brief struct representing a trie node.
|
||||
*/
|
||||
struct Node {
|
||||
std::unordered_map<char16_t, std::shared_ptr<Node>>
|
||||
children; ///< unordered map with key type char16_t and value is a
|
||||
///< shared pointer type of Node
|
||||
bool word_end = false; ///< boolean variable to represent the node end
|
||||
};
|
||||
|
||||
std::shared_ptr<Node> root_node =
|
||||
std::make_shared<Node>(); ///< declaring root node of trie
|
||||
|
||||
public:
|
||||
///< Constructor
|
||||
Trie() = default;
|
||||
|
||||
/**
|
||||
* @brief insert the string into the trie
|
||||
* @param word string to insert in the trie
|
||||
*/
|
||||
void insert(const std::string& word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
curr->children[ch] = std::make_shared<Node>();
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
|
||||
if (!curr->word_end && curr != root_node) {
|
||||
curr->word_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief search a word/string inside the trie
|
||||
* @param word string to search for
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool search(const std::string& word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return false;
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
if (!curr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (curr->word_end) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief search a word/string that starts with a given prefix
|
||||
* @param prefix string to search for
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool startwith(const std::string& prefix) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : prefix) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return false;
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief delete a word/string from a trie
|
||||
* @param word string to delete from trie
|
||||
*/
|
||||
void delete_word(std::string word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
std::stack<std::shared_ptr<Node>> nodes;
|
||||
int cnt = 0;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return;
|
||||
}
|
||||
if (curr->word_end) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
nodes.push(curr->children[ch]);
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
// Delete only when it's a word, and it has children after
|
||||
// or prefix in the line
|
||||
if (nodes.top()->word_end) {
|
||||
nodes.top()->word_end = false;
|
||||
}
|
||||
// Delete only when it has no children after
|
||||
// and also no prefix in the line
|
||||
while (!(nodes.top()->word_end) && nodes.top()->children.empty()) {
|
||||
nodes.pop();
|
||||
nodes.top()->children.erase(word.back());
|
||||
word.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief helper function to predict/recommend words that starts with a
|
||||
* given prefix from the end of prefix's node iterate through all the child
|
||||
* nodes by recursively appending all the possible words below the trie
|
||||
* @param prefix string to recommend the words
|
||||
* @param element node at the end of the given prefix
|
||||
* @param results list to store the all possible words
|
||||
* @returns list of recommended words
|
||||
*/
|
||||
std::vector<std::string> get_all_words(std::vector<std::string> results,
|
||||
const std::shared_ptr<Node>& element,
|
||||
std::string prefix) {
|
||||
if (element->word_end) {
|
||||
results.push_back(prefix);
|
||||
}
|
||||
if (element->children.empty()) {
|
||||
return results;
|
||||
}
|
||||
for (auto const& x : element->children) {
|
||||
std::string key = "";
|
||||
key = x.first;
|
||||
prefix += key;
|
||||
|
||||
results =
|
||||
get_all_words(results, element->children[x.first], prefix);
|
||||
|
||||
prefix.pop_back();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief predict/recommend a word that starts with a given prefix
|
||||
* @param prefix string to search for
|
||||
* @returns list of recommended words
|
||||
*/
|
||||
std::vector<std::string> predict_words(const std::string& prefix) {
|
||||
std::vector<std::string> result;
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
// traversing until the end of the given prefix in trie
|
||||
|
||||
for (char ch : prefix) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
|
||||
// if the given prefix is the only word without children
|
||||
if (curr->word_end && curr->children.empty()) {
|
||||
result.push_back(prefix);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = get_all_words(
|
||||
result, curr,
|
||||
prefix); ///< iteratively and recursively get the recommended words
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace trie_using_hashmap
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::trie_using_hashmap::Trie obj;
|
||||
// Inserting data into trie using the insert
|
||||
// method and testing it with search method
|
||||
obj.insert("app");
|
||||
obj.insert("abscond");
|
||||
obj.insert("about");
|
||||
obj.insert("apps");
|
||||
obj.insert("apen");
|
||||
obj.insert("apples");
|
||||
obj.insert("apple");
|
||||
obj.insert("approach");
|
||||
obj.insert("bus");
|
||||
obj.insert("buses");
|
||||
obj.insert("Apple");
|
||||
obj.insert("Bounce");
|
||||
|
||||
assert(!obj.search("appy"));
|
||||
std::cout << "appy is not a word in trie" << std::endl;
|
||||
|
||||
assert(!obj.search("car"));
|
||||
std::cout << "car is not a word in trie" << std::endl;
|
||||
assert(obj.search("app"));
|
||||
assert(obj.search("apple"));
|
||||
assert(obj.search("apples"));
|
||||
assert(obj.search("apps"));
|
||||
assert(obj.search("apen"));
|
||||
assert(obj.search("approach"));
|
||||
assert(obj.search("about"));
|
||||
assert(obj.search("abscond"));
|
||||
assert(obj.search("bus"));
|
||||
assert(obj.search("buses"));
|
||||
assert(obj.search("Bounce"));
|
||||
assert(obj.search("Apple"));
|
||||
|
||||
std::cout << "All the Inserted words are present in the trie" << std::endl;
|
||||
|
||||
// test for startwith prefix method
|
||||
assert(!obj.startwith("approachs"));
|
||||
assert(obj.startwith("approach"));
|
||||
assert(obj.startwith("about"));
|
||||
assert(!obj.startwith("appy"));
|
||||
assert(obj.startwith("abscond"));
|
||||
assert(obj.startwith("bus"));
|
||||
assert(obj.startwith("buses"));
|
||||
assert(obj.startwith("Bounce"));
|
||||
assert(obj.startwith("Apple"));
|
||||
assert(obj.startwith("abs"));
|
||||
assert(obj.startwith("b"));
|
||||
assert(obj.startwith("bus"));
|
||||
assert(obj.startwith("Bo"));
|
||||
assert(obj.startwith("A"));
|
||||
assert(!obj.startwith("Ca"));
|
||||
|
||||
assert(!obj.startwith("C"));
|
||||
|
||||
std::cout << "All the tests passed for startwith method" << std::endl;
|
||||
|
||||
// test for predict_words/recommendation of words based on a given prefix
|
||||
|
||||
std::vector<std::string> pred_words = obj.predict_words("a");
|
||||
|
||||
for (const std::string& str : obj.predict_words("a")) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
assert(pred_words.size() == 8);
|
||||
std::cout << "Returned all words that start with prefix a " << std::endl;
|
||||
pred_words = obj.predict_words("app");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 5);
|
||||
std::cout << "Returned all words that start with prefix app " << std::endl;
|
||||
pred_words = obj.predict_words("A");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 1);
|
||||
std::cout << "Returned all words that start with prefix A " << std::endl;
|
||||
pred_words = obj.predict_words("bu");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 2);
|
||||
std::cout << "Returned all words that start with prefix bu " << std::endl;
|
||||
|
||||
// tests for delete method
|
||||
|
||||
obj.delete_word("app");
|
||||
assert(!obj.search("app"));
|
||||
std::cout << "word app is deleted sucessful" << std::endl;
|
||||
|
||||
pred_words = obj.predict_words("app");
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
assert(pred_words.size() == 4);
|
||||
std::cout << "app is deleted sucessful" << std::endl;
|
||||
|
||||
// test case for Chinese language
|
||||
|
||||
obj.insert("苹果");
|
||||
assert(obj.startwith("苹"));
|
||||
pred_words = obj.predict_words("h");
|
||||
|
||||
assert(pred_words.size() == 0);
|
||||
std::cout << "No word starts with prefix h in trie" << std::endl;
|
||||
|
||||
std::cout << "All tests passed" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementaions
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user