#ifndef LINKEDLIST_H #define LINKEDLIST_H #include #include #include #include #include #include /** * @brief Concept that ensures a type supports equality comparison via operator==. * * @tparam T Type to be checked. */ template concept HasEqual = requires (T a, T b) { { a == b } -> std::convertible_to; }; /** * @brief Concept defining the requirements for types supported by LinkedList. * * A valid type must: * - Be formattable (usable with std::print / std::format) * - Support equality comparison * * @tparam T Type to be checked. */ template concept LinkedListSupported = std::formattable && std::equality_comparable && HasEqual; /** * @brief Error codes for LinkedList operations. */ enum class LinkedListErr { LINKEDLIST_OK, /**< Operation completed successfully */ LINKEDLIST_BAD_ALLOC, /**< Memory allocation failed */ LINKEDLIST_OUT_OF_BOUNDS, /**< Index is out of valid range */ LINKEDLIST_INVALID_SIZE, /**< Invalid size provided */ LINKEDLIST_NOT_FOUND, /**< Element not found */ LINKEDLIST_EMPTY, /**< The list is empty */ }; /** * @brief Node of a singly linked list. * * @tparam T Type of data stored in the node. */ template class Node { public: T data; /**< Data stored in the node */ Node* next; /**< Pointer to the next node */ }; /** * @brief Implementation of a singly linked list. * * A dynamic data structure that stores elements of type T * using nodes connected via pointers. * * @tparam T Type of elements stored in the list. */ template class LinkedList { private: size_t size; /**< Number of elements in the list */ Node* head; /**< Pointer to the first node */ Node* tail; /**< Pointer to the last node */ public: /** * @brief Constructs an empty linked list. */ LinkedList(); /** * @brief Destroys the linked list. * * Releases all dynamically allocated nodes. */ ~LinkedList(); /** * @brief Retrieves the element at a given index. * * @param index Position of the element. * @return std::expected The element or an error code. */ std::expected get(size_t index); /** * @brief Appends an element to the end of the list. * * @param value Value to insert. * @return LinkedListErr Status code of the operation. */ LinkedListErr append(const T& value); /** * @brief Inserts an element at the beginning of the list. * * @param value Value to insert. * @return LinkedListErr Status code of the operation. */ LinkedListErr prepend(const T& value); /** * @brief Removes and returns the last element of the list. * * @return std::expected Removed element or an error. */ std::expected pop(); /** * @brief Removes the element at a specific index. * * @param index Index of the element to remove. * @return LinkedListErr Status code. */ LinkedListErr remove(size_t index); /** * @brief Updates the value of an element at a given index. * * @param index Index of the element. * @param value New value. * @return LinkedListErr Status code. */ LinkedListErr set(size_t index, const T& value); /** * @brief Inserts a value at a specified index. * * @param index Target position. * @param value Value to insert. * @return LinkedListErr Status code. */ LinkedListErr insert(size_t index, const T& value); /** * @brief Searches for a value in the list. * * @param value Value to search for. * @return std::expected Index of the element or error. */ std::expected find(const T& value); /** * @brief Prints all elements in the list. * * @return LinkedListErr Status code. */ LinkedListErr print(); /** * @brief Checks whether the list is empty. * * @return true if the list contains no elements, false otherwise. */ bool is_empty(); /** * @brief Returns the number of elements in the list. * * @return size_t Number of stored elements. */ size_t len(); }; template LinkedList::LinkedList() { size = 0; head = nullptr; tail = nullptr; } template LinkedList::~LinkedList() { Node *current = this->head; while (current != nullptr) { Node *temp = current; current = current->next; delete temp; } } template std::expected LinkedList::get(size_t index) { if (index >= this->size) { return std::unexpected(LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS); } if (index == this->size - 1) { return tail->data; } Node *current = this->head; for (size_t i = 0; i < index ; i++) { current = current->next; } return current->data; } template LinkedListErr LinkedList::set(size_t index, const T& val) { if (index >= this->size) { return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS; } if (index == this->size - 1) { this->tail->data = val; } Node *current = this->head; for (size_t i = 0; i < index; i++) { current = current->next; } current->data = val; return LinkedListErr::LINKEDLIST_OK; } template LinkedListErr LinkedList::insert(size_t index, const T& value) { if (this->is_empty()) { return this->append(value); } if (index >= this->size) { return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS; } // If index is head if (index == 0) { return this->prepend(value); } // If index is tail if (index == this->size - 1) { return this->append(value); } Node *current = this->head; for (size_t i = 1; i < index; i++) { current = current->next; } Node *new_node = nullptr; try { new_node = new Node; } catch(const std::bad_alloc&) { return LinkedListErr::LINKEDLIST_BAD_ALLOC; } new_node->data = value; new_node->next = current->next->next; current->next = new_node; return LinkedListErr::LINKEDLIST_OK; } template LinkedListErr LinkedList::append(const T& value) { Node *new_node = nullptr; try { new_node = new Node; } catch(std::bad_alloc&) { return LinkedListErr::LINKEDLIST_BAD_ALLOC; } new_node->data = value; new_node->next = nullptr; if (this->head == nullptr && this->tail == nullptr) { this->head = new_node; this->tail = this->head; } else { this->tail->next = new_node; this->tail = this->tail->next; } this->size += 1; return LinkedListErr::LINKEDLIST_OK; } template LinkedListErr LinkedList::prepend(const T& value) { Node *new_node = nullptr; try { new_node = new Node; } catch(std::bad_alloc&) { return LinkedListErr::LINKEDLIST_BAD_ALLOC; } new_node->data = value; new_node->next = nullptr; if (this->head == nullptr && this->tail == nullptr) { this->head = new_node; this->tail = this->head; } else { new_node->next = this->head; this->head = new_node; } this->size += 1; return LinkedListErr::LINKEDLIST_OK; } template std::expected LinkedList::pop() { if (this->size == 0) { return std::unexpected(LinkedListErr::LINKEDLIST_EMPTY); } if (this->head->next == nullptr) { T return_val = this->head->data; delete this->head; this->head = nullptr; this->size--; return return_val; } Node* current = this->head; while (current->next->next != nullptr) { current = current->next; } T return_val = current->next->data; delete current->next; current->next = nullptr; this->size--; return return_val; } template LinkedListErr LinkedList::remove(size_t index) { if (index >= this->size) { return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS; } if (index == 0) { Node* tmp = this->head; this->head = this->head->next; delete tmp; this->size--; return LinkedListErr::LINKEDLIST_OK; } Node* current = this->head; for (size_t i = 1; i < index; ++i) { current = current->next; } Node* tmp = current->next; current->next = tmp->next; if(tmp == this->tail) { this->tail = current; } delete tmp; this->size--; return LinkedListErr::LINKEDLIST_OK; } template std::expected LinkedList::find(const T& value) { Node *current = this->head; size_t count = 0; while (current != nullptr) { if (current->data == value) { return count; } count++; current = current->next; } return std::unexpected(LinkedListErr::LINKEDLIST_NOT_FOUND); } template LinkedListErr LinkedList::print() { if (this->size == 0) { return LinkedListErr::LINKEDLIST_OK; } std::println("[LL]"); std::println(" |\n v"); Node *current = this->head; while (current != nullptr) { std::println("Node({})", current->data); std::println(" |"); std::println(" v"); current = current->next; } std::println("NULL"); return LinkedListErr::LINKEDLIST_OK; } template size_t LinkedList::len() { return this->size; } #endif // !