// linked_list.h #ifndef LINKED_LIST_H_INCLUDED #define LINKED_LIST_H_INCLUDED // note: implementation required for pass class linked_list { linked_list(linked_list&& rhs) = delete; linked_list(const linked_list& rhs) = delete; linked_list& operator=(const linked_list &rhs) = delete; linked_list& operator=(linked_list&& rhs) = delete; struct node { node(); node(int value, node* next); int value_ = 0; node* next_ = nullptr; }; public: linked_list(); ~linked_list(); void push_front(int value); void push_back(int value); void insert(int value, int index); void pop_front(); void pop_back(); void remove(int index); void clear(); int size() const noexcept; int front() const noexcept; int back() const noexcept; int at(int index) const noexcept; node* getNodeAt(int index) const noexcept; bool empty() const noexcept; bool isInRange(int index) const noexcept; private: int count_; node *head_; node *tail_; }; #endif // !LINKED_LIST_H_INCLUDED // linked_list.cpp #include "linked_list.h" linked_list::node::node(int value, node *next) : value_(value) , next_(next) { } linked_list::linked_list() : count_(0) , head_(nullptr) , tail_(nullptr) { } linked_list::~linked_list() { clear(); } void linked_list::push_front(int value) { head_ = new node{ value, head_ }; if (empty()){ tail_ = head_; } count_++; } void linked_list::push_back(int value) { node* Tmp_Node = new node{ value, nullptr }; if (empty()) { head_ = Tmp_Node; }else{ tail_->next_ = Tmp_Node; } tail_ = Tmp_Node; count_++; } void linked_list::insert(int value, int index) { if (!(isInRange(index) || index == count_)){ //WARNING, count_ should not be valid. The test is wrong. return; } if (index == 0){ return push_front(value); } if (index == count_) { // If index is after tail. TODO: probably a logic error. return push_back(value); } node* infront = getNodeAt(index - 1); node* after = infront->next_; infront->next_ = new node{ value, after }; count_++; } void linked_list::pop_front() { if (empty()) { return; //TODO: do we want pop_front on empty containers to be allowed? consider exceptions } if (count_ == 1) // If the list has only 1 node. { delete head_; head_ = nullptr; tail_ = nullptr; count_--; return; } else // If the list has more than 1 node. { node* Tmp_Node; Tmp_Node = head_->next_; delete head_; head_ = Tmp_Node; count_--; return; } } void linked_list::pop_back(){ if (empty()) { return;//TODO: pop back on empty should be undefined behavior } /*node* secondToLast = getNodeAt(size() - 1); delete tail_; secondToLast->next_ = nullptr; tail_ = secondToLast; count_--; if (empty()) { head_ = nullptr; tail_ = nullptr; }*/ if (count_ == 1) // If the list has 1 node. { delete tail_; tail_ = nullptr; head_ = nullptr; count_--; return; } else // If list has more than 1 node. { node* Tmp_CurrentNode = head_; node* Tmp_PreviousNode = head_; // Initialized as head just to avoid warnings. int IterationCounter = 0; while (Tmp_CurrentNode != tail_) { if (IterationCounter == (count_ - 2)) // Captures the before last node. { Tmp_PreviousNode = Tmp_CurrentNode; } Tmp_CurrentNode = Tmp_CurrentNode->next_; IterationCounter++; } delete Tmp_CurrentNode; Tmp_PreviousNode->next_ = nullptr; tail_ = Tmp_PreviousNode; count_--; return; } } void linked_list::remove(int index) { if (isInRange(index)) // Validates if index exists in the list. { node* Tmp_Node; if (index == 0) // If index is head. { if (head_->next_ == nullptr) // If there is nothing after the head. { delete head_; head_ = nullptr; tail_ = nullptr; count_--; return; } else // If there is something after the head. { Tmp_Node = head_->next_; delete head_; head_ = Tmp_Node; count_--; return; } } node* Tmp_CurrentNode = head_; node* Tmp_PreviousNode = head_; // Initialized as head just to avoid warnings. int IterationCounter = 0; if (index == (count_ - 1)) // If index is tail. { while (IterationCounter != index) { if (IterationCounter == (index - 1)) // Captures the previous node. { Tmp_PreviousNode = Tmp_CurrentNode; } Tmp_CurrentNode = Tmp_CurrentNode->next_; IterationCounter++; } delete Tmp_CurrentNode; Tmp_PreviousNode->next_ = nullptr; tail_ = Tmp_PreviousNode; count_--; return; } else // If index is in the middle. { while (IterationCounter != index) { if (IterationCounter == (index - 1)) // Captures the previous node. { Tmp_PreviousNode = Tmp_CurrentNode; } Tmp_CurrentNode = Tmp_CurrentNode->next_; IterationCounter++; } Tmp_Node = Tmp_CurrentNode->next_; delete Tmp_CurrentNode; Tmp_PreviousNode->next_ = Tmp_Node; count_--; return; } } } void linked_list::clear(){ while (!empty()) { pop_back(); //assumes pop_back assigns nullptr to members when empty } } int linked_list::size() const noexcept { return count_; } int linked_list::front() const noexcept { if (head_ == nullptr){ return -1; //WARNING: error sentinel value is a valid int value. } return head_->value_; } int linked_list::back() const noexcept { if (tail_ == nullptr){ return -1; //WARNING: error sentinel value is a valid int value. } return tail_->value_; } int linked_list::at(int index) const noexcept { if (!isInRange(index)) { return -1; //WARNING: error sentinel value is a valid value. //invalid indexes should perhaps throw an exception, or assert. } node* tmp = getNodeAt(index); return tmp ? tmp->value_ : -1; } linked_list::node* linked_list::getNodeAt(int index) const noexcept { if (!isInRange(index)) { return nullptr; } node* temp = head_; for (int i = 0; i < index && temp->next_; i++) { temp = temp->next_; } return temp; } bool linked_list::empty() const noexcept{ return count_ <= 0; //TODO: change count_ to std::size_t } bool linked_list::isInRange(int index) const noexcept{ return !empty() && index > -1 && index < size(); }