430 lines
9.9 KiB
C++
430 lines
9.9 KiB
C++
#ifndef LINKEDLIST_H
|
|
#define LINKEDLIST_H
|
|
|
|
#include <concepts>
|
|
#include <cstdlib>
|
|
#include <expected>
|
|
#include <format>
|
|
#include <new>
|
|
#include <print>
|
|
|
|
/**
|
|
* @brief Concept that ensures a type supports equality comparison via operator==.
|
|
*
|
|
* @tparam T Type to be checked.
|
|
*/
|
|
template <typename T>
|
|
concept HasEqual = requires (T a, T b) {
|
|
{ a == b } -> std::convertible_to<bool>;
|
|
};
|
|
|
|
/**
|
|
* @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 <typename T>
|
|
concept LinkedListSupported =
|
|
std::formattable<T, char> &&
|
|
std::equality_comparable<T> &&
|
|
HasEqual<T>;
|
|
|
|
|
|
/**
|
|
* @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<LinkedListSupported T>
|
|
class Node {
|
|
public:
|
|
T data; /**< Data stored in the node */
|
|
Node<T>* 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<LinkedListSupported T>
|
|
class LinkedList {
|
|
private:
|
|
size_t size; /**< Number of elements in the list */
|
|
Node<T>* head; /**< Pointer to the first node */
|
|
Node<T>* 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<T, LinkedListErr> The element or an error code.
|
|
*/
|
|
std::expected<T, LinkedListErr> 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<T, LinkedListErr> Removed element or an error.
|
|
*/
|
|
std::expected<T, LinkedListErr> 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<size_t, LinkedListErr> Index of the element or error.
|
|
*/
|
|
std::expected<size_t, LinkedListErr> 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 <LinkedListSupported T>
|
|
LinkedList<T>::LinkedList() {
|
|
size = 0;
|
|
head = nullptr;
|
|
tail = nullptr;
|
|
}
|
|
|
|
template <LinkedListSupported T>
|
|
LinkedList<T>::~LinkedList() {
|
|
Node<T> *current = this->head;
|
|
|
|
while (current != nullptr) {
|
|
Node<T> *temp = current;
|
|
current = current->next;
|
|
delete temp;
|
|
}
|
|
}
|
|
|
|
template <LinkedListSupported T>
|
|
std::expected<T, LinkedListErr> LinkedList<T>::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<T> *current = this->head;
|
|
for (size_t i = 0; i < index ; i++) {
|
|
current = current->next;
|
|
}
|
|
|
|
return current->data;
|
|
}
|
|
|
|
template <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::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<T> *current = this->head;
|
|
for (size_t i = 0; i < index; i++) {
|
|
current = current->next;
|
|
}
|
|
|
|
current->data = val;
|
|
return LinkedListErr::LINKEDLIST_OK;
|
|
}
|
|
|
|
template <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::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<T> *current = this->head;
|
|
for (size_t i = 1; i < index; i++) {
|
|
current = current->next;
|
|
}
|
|
|
|
Node<T> *new_node = nullptr;
|
|
|
|
try {
|
|
new_node = new Node<T>;
|
|
} 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 <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::append(const T& value) {
|
|
Node<T> *new_node = nullptr;
|
|
|
|
try {
|
|
new_node = new Node<T>;
|
|
} 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 <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::prepend(const T& value) {
|
|
Node<T> *new_node = nullptr;
|
|
|
|
try {
|
|
new_node = new Node<T>;
|
|
} 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 <LinkedListSupported T>
|
|
std::expected<T, LinkedListErr> LinkedList<T>::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<T>* 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 <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::remove(size_t index) {
|
|
if (index >= this->size) {
|
|
return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS;
|
|
}
|
|
|
|
if (index == 0) {
|
|
Node<T>* tmp = this->head;
|
|
this->head = this->head->next;
|
|
delete tmp;
|
|
this->size--;
|
|
return LinkedListErr::LINKEDLIST_OK;
|
|
}
|
|
|
|
Node<T>* current = this->head;
|
|
|
|
for (size_t i = 1; i < index; ++i) {
|
|
current = current->next;
|
|
}
|
|
|
|
Node<T>* tmp = current->next;
|
|
current->next = tmp->next;
|
|
|
|
if(tmp == this->tail) {
|
|
this->tail = current;
|
|
}
|
|
|
|
delete tmp;
|
|
this->size--;
|
|
|
|
return LinkedListErr::LINKEDLIST_OK;
|
|
}
|
|
|
|
|
|
template <LinkedListSupported T>
|
|
std::expected<size_t, LinkedListErr> LinkedList<T>::find(const T& value) {
|
|
Node<T> *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 <LinkedListSupported T>
|
|
LinkedListErr LinkedList<T>::print() {
|
|
if (this->size == 0) {
|
|
return LinkedListErr::LINKEDLIST_OK;
|
|
}
|
|
|
|
std::println("[LL]");
|
|
std::println(" |\n v");
|
|
Node<T> *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 <LinkedListSupported T>
|
|
size_t LinkedList<T>::len() {
|
|
return this->size;
|
|
}
|
|
|
|
|
|
#endif // !
|