From 1f0eff28d07a21b2426f1be0aab4f8f683e9f736 Mon Sep 17 00:00:00 2001 From: Alvin Philips Date: Tue, 2 Nov 2021 22:33:49 +0530 Subject: [PATCH] fix: Circular linked list (#1825) * Create reverse_binary_tree.cpp * Added documentation Added Documentation for the level_order_traversal() function, and implemented a print() function to display the tree to STDOUT * Added documentation * Renamed tests to test * Fixed issue with incorrect using statement * updating DIRECTORY.md * clang-format and clang-tidy fixes for fb86292d * Added Test cases * Update operations_on_datastructures/reverse_binary_tree.cpp Co-authored-by: David Leal * Update operations_on_datastructures/reverse_binary_tree.cpp Co-authored-by: David Leal * Update operations_on_datastructures/reverse_binary_tree.cpp Co-authored-by: David Leal * Update operations_on_datastructures/reverse_binary_tree.cpp Co-authored-by: David Leal * Update operations_on_datastructures/reverse_binary_tree.cpp Co-authored-by: David Leal * Changed int to int64_t * Updated documentation wording * Rewrote node struct and file structure * Added Circular Linked List * Update doc * Wrote bulk of code, added documentation * clang-format and clang-tidy fixes for 06f11f1e * Update operations_on_datastructures/circular_linked_list.cpp Co-authored-by: David Leal * Update operations_on_datastructures/circular_linked_list.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for 25ed3ea0 * Added parameter documentation * Added destructor for CircularLinkedList * Fixed memory bug * Update circular_linked_list.cpp * Update circular_linked_list.cpp * Added move constructor and assignment operator * clang-format and clang-tidy fixes for b36e4736 * Fixed copy and move operators/constructors and added documentation * clang-format and clang-tidy fixes for 24d3b9c2 * Added missing returns Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal Co-authored-by: Abhinn Mishra <49574460+mishraabhinn@users.noreply.github.com> --- .../circular_linked_list.cpp | 411 ++++++++++++++---- 1 file changed, 331 insertions(+), 80 deletions(-) diff --git a/operations_on_datastructures/circular_linked_list.cpp b/operations_on_datastructures/circular_linked_list.cpp index 1119bb5e7..bc97d1a75 100644 --- a/operations_on_datastructures/circular_linked_list.cpp +++ b/operations_on_datastructures/circular_linked_list.cpp @@ -1,97 +1,348 @@ -#include -using namespace std; +/** + * @file + * @brief Implementation for a [Circular Linked + * List](https://www.geeksforgeeks.org/circular-linked-list/). + * @details A Circular Linked List is a variation on the regular linked list, in + * which the last node has a pointer to the first node, which creates a full + * circle. Consequently, this allows any node to be used as the starting point + * for the list. + * @author [Alvin](https://github.com/polarvoid) + */ -struct node { - int val; - node *next; +#include /// for assert +#include /// for IO operations +#include /// for std::vector + +/** + * @namespace operations_on_datastructures + * @brief Operations on Data Structures + */ +namespace operations_on_datastructures { + +/** + * @namespace circular_linked_list + * @brief Functions for the [Circular Linked + * List](https://www.geeksforgeeks.org/circular-linked-list/) implementation + */ +namespace circular_linked_list { + +/** + * @brief A Node struct that represents a single Node in a Binary Tree + */ +struct Node { + int64_t data; ///< The value of the Node + Node* next; ///< The Node's successor + /** + * @brief Creates a new Node with some initial data + * @param _data Value of Node + */ + explicit Node(int64_t _data) { + data = _data; ///< Set value of Node data + next = nullptr; ///< Initialize successor + } + /** + * @brief Creates a new Node with initial data and a successor + * @param _data Value of Node + * @param _next Pointer to the next Node + */ + explicit Node(int64_t _data, Node* _next) { + data = _data; ///< Set value of Node data + next = _next; ///< Initialize successor + } }; -node *start; +/** + * @brief A class that implements a Circular Linked List. + */ +class CircularLinkedList { + private: + Node* root; ///< Pointer to the root Node + Node* end{}; ///< Pointer to the last Node -void insert(int x) { - node *t = start; - - if (start != NULL) { - while (t->next != start) { - t = t->next; + public: + /** + * @brief Creates an empty CircularLinkedList. + */ + CircularLinkedList() { + root = nullptr; + end = nullptr; + } + /** + * @brief Copy constructor for CircularLinkedList. + */ + CircularLinkedList(const CircularLinkedList& copy) { + erase(); + root = nullptr; + Node* node = copy.root; + while (node != nullptr) { + insert(node->data); + node = node->next; } - node *n = new node; - t->next = n; - n->val = x; - n->next = start; - } else { - node *n = new node; - n->val = x; - start = n; - n->next = start; } -} - -void remove(int x) { - node *t = start; - node *p; - while (t->val != x) { - p = t; - t = t->next; + /** + * @brief Move constructor for CircularLinkedList + * @param source rvalue reference to a Circular Linked List + */ + CircularLinkedList(CircularLinkedList&& source) noexcept { + root = source.root; + end = source.end; + source.root = nullptr; + source.end = nullptr; } - p->next = t->next; - delete t; -} - -void search(int x) { - node *t = start; - int found = 0; - while (t->next != start) { - if (t->val == x) { - cout << "\nFound"; - found = 1; - break; + /** + * @brief Copy assignment operator + * @param other Reference to a Circular Linked List + * @returns Reference to CircularLinkedList + */ + CircularLinkedList& operator=(const CircularLinkedList& other) { + erase(); + root = nullptr; + Node* node = other.root; + while (node != nullptr) { + insert(node->data); + node = node->next; } - t = t->next; + return *this; } - if (found == 0) { - cout << "\nNot Found"; + /** + * @brief Move assignment operator + * @param other rvalue reference to a Circular Linked List + * @returns Reference to CircularLinkedList + */ + CircularLinkedList& operator=(CircularLinkedList&& other) noexcept { + root = other.root; + end = other.end; + other.root = nullptr; + other.end = nullptr; + return *this; } + /** + * @brief Cleans up memory when destroyed + */ + ~CircularLinkedList() { erase(); } + /** + * Iteratively frees each node in the Circular Linked List from the heap + */ + void erase() { + if (root == nullptr) { + return; + } + Node* node = root; + do { + Node* temp = node; + node = node->next; + delete (temp); + } while (node != root); + root = nullptr; + end = nullptr; + } + /** + * @brief Inserts all the values from a vector into the Circular Linked List + * @details Goes through each element in the vector sequentially, inserting + * it into the list + * @param values The vector of integer values that is to be inserted + * @returns void + */ + void insert(const std::vector& values) { + for (int64_t value : values) { + insert(value); + } + } + /** + * @brief Inserts a single value into the Circular Linked List + * @details Creates a Node with the given value, pointing to the root Node + * and inserts it into the list + * @param data The integer valus to be inserted + * @returns void + */ + void insert(int64_t data) { + Node* node = new Node(data, root); + insert(node); + } + /** + * @brief Inserts a given Node into the Circular Linked List + * @details Checks wheter the list is empty, and inserts the Node, modifying + * the end pointer + * @param node The Node that is to be inserted + * @returns void + */ + void insert(Node* node) { + if (root == nullptr) { + root = node; ///< Set node as the root + node->next = root; ///< Point node to itself + end = root; ///< Set the end to the root + } else { + end->next = node; ///< Append node to the end + node->next = root; ///< Set the next value to the root + end = node; ///< Make end point to node + } + } + /** + * @brief Prints the values of the Circular Linked List, beginning from the + * root Node + * @details Goes through each Node from the root and prints them out in + * order + * @returns void + */ + void print() { print(root); } + /** + * @brief Prints the values of the Circular Linked List, beginning from a + * given Node to be used as the root + * @details Goes through each Node from the given Node and prints them out + * in order. If the list is empty, it prints the message 'Empty List!' + * @param root The Node to start at + * @returns void + */ + void print(Node* root) { + Node* temp = root; + if (root == nullptr) { + std::cout << "Empty List!\n"; + return; + } + do { + std::cout << temp->data << " "; + temp = temp->next; + } while (temp != root); + std::cout << "\n"; + } + /** + * @brief Returns a std::vector of the values of the Circular Linked List + * @details Starting from the root Node, appends each value of the list to a + * std::vector and returns it + * @returns A std::vector of the list's values + */ + std::vector values() { return values(root); } + /** + * @brief Returns a std::vector of the values of the Circular Linked List, + * beginning from a given Node + * @details Starting from a given Node, appends each value of the list to a + * std::vector and returns it + * @param root The Node to start at + * @returns A std::vector of the list's values + */ + std::vector values(Node* root) { + std::vector res; + if (root == nullptr) { + return res; ///< Return empty vector + } + Node* temp = root; + do { + res.push_back(temp->data); + temp = temp->next; + } while (temp != root); + return res; + } +}; + +} // namespace circular_linked_list + +} // namespace operations_on_datastructures + +/** + * @namespace tests + * @brief Testcases to check Circular Linked List. + */ +namespace tests { +using operations_on_datastructures::circular_linked_list::CircularLinkedList; +using operations_on_datastructures::circular_linked_list::Node; +/** + * @brief A Test to check a single value + * @returns void + */ +void test1() { + std::cout << "TEST CASE 1\n"; + std::cout << "Intialized a = {2}\n"; + std::cout << "Expected result: {2}\n"; + CircularLinkedList a; + std::vector res = {2}; + a.insert(2); + assert(a.values() == res); + a.print(); + std::cout << "TEST PASSED!\n\n"; +} +/** + * @brief A Test to check a few values + * @returns void + */ +void test2() { + std::cout << "TEST CASE 2\n"; + std::cout << "Intialized a = {2, 5, 6}\n"; + std::cout << "Expected result: {2, 5, 6}\n"; + CircularLinkedList a; + std::vector res = {2, 5, 6}; + a.insert(2); + a.insert(5); + a.insert(6); + assert(a.values() == res); + a.print(); + std::cout << "TEST PASSED!\n\n"; +} +/** + * @brief A Test to check an input array + * @returns void + */ +void test3() { + std::cout << "TEST CASE 3\n"; + std::cout << "Intialized a = {2, 7, 8, 3, 2, 6}\n"; + std::cout << "Expected result: {2, 7, 8, 3, 2, 6}\n"; + CircularLinkedList a; + std::vector res = {2, 7, 8, 3, 2, 6}; + a.insert({2, 7, 8, 3, 2, 6}); + a.print(); + assert(a.values() == res); + std::cout << "TEST PASSED!\n\n"; +} +/** + * @brief A Test to check using a specific Node as the starting point + * @returns void + */ +void test4() { + std::cout << "TEST CASE 4\n"; + std::cout << "Intialized a = {2, 5}\n"; + std::cout << "Expected result: {5, 2}\n"; + CircularLinkedList a; + std::vector res = {5, 2}; + a.insert(2); + Node* start = new Node(5); ///< Node we will start printing from + a.insert(start); + assert(a.values(start) == res); + a.print(start); + std::cout << "TEST PASSED!\n\n"; } -void show() { - node *t = start; - do { - cout << t->val << "\t"; - t = t->next; - } while (t != start); +/** + * @brief A Test to check an empty list + * @returns void + */ +void test5() { + std::cout << "TEST CASE 5\n"; + std::cout << "Intialized a = {}\n"; + std::cout << "Expected result: Empty List!\n"; + CircularLinkedList a; + std::vector res = {}; + assert(a.values() == res); + a.print(); + std::cout << "TEST PASSED!\n\n"; +} +} // namespace tests + +/** + * @brief Function to test the correctness of the Circular Linked List + * @returns void + */ +static void test() { + tests::test1(); + tests::test2(); + tests::test3(); + tests::test4(); + tests::test5(); } +/** + * @brief main function + * @returns 0 on exit + */ int main() { - int choice, x; - do { - cout << "\n1. Insert"; - cout << "\n2. Delete"; - cout << "\n3. Search"; - cout << "\n4. Print"; - cout << "\n\nEnter you choice : "; - cin >> choice; - switch (choice) { - case 1: - cout << "\nEnter the element to be inserted : "; - cin >> x; - insert(x); - break; - case 2: - cout << "\nEnter the element to be removed : "; - cin >> x; - remove(x); - break; - case 3: - cout << "\nEnter the element to be searched : "; - cin >> x; - search(x); - break; - case 4: - show(); - break; - } - } while (choice != 0); - + test(); // run self-test implementations return 0; }