// Implantation of the Circular Doubly Linked List #ifndef TCIRCULARDOUBLYLINKEDLISTTEMPLATE_HPP #define TCIRCULARDOUBLYLINKEDLISTTEMPLATE_HPP #pragma once #include "TDoublyLinkedListTemplate.hpp" template class TCircularDoublyLinkedList : public TDoublyLinkedList { private: // Helper to remove a node, updating both next and prev pointers void InternalRemoveNode(TNode* aNodeToDelete); protected: // --- New Member Variable --- TNode* cursor; // Points to the "current" node in the list. public: using TSingleLinkedList::Append; using TSingleLinkedList::Prepend; // --- Constructor & Destructor --- TCircularDoublyLinkedList(bool aIsDataOwner); virtual ~TCircularDoublyLinkedList() override; // --- New Cursor Management Methods --- void ResetCursor(); T GetCursorData() const; void AdvanceCursor(int aSteps = 1); void RewindCursor(int aSteps = 1); int GetCursorIndex() const; // --- Overridden Virtual Methods --- // For maintaining the circular link void Append(const T&) override; void Prepend(const T&) override; void Remove(const T&) override; void RemoveAll(const T&) override; T RemoveLast() override; void Reverse() override; void ReverseSublist(int, int) override; void Merge(TSingleLinkedList& otherList) override; // For avoiding infinite loops bool Contains(const T&) const override; T Search(const T&, FCheckNode) const override; void ForEach(FVisitNode) const override; TNode* GetMiddle() const override; }; // Constructor: Establishes the initial circular link where the head points to itself. template TCircularDoublyLinkedList::TCircularDoublyLinkedList(bool aIsDataOwner) : TDoublyLinkedList(aIsDataOwner) { // An empty circular list's dummy head points to itself. this->head->SetNext(this->head); this->head->SetPrev(this->head); this->cursor = this->head; // Cursor also starts at the head. } // Destructor: The parent destructors are virtual and will handle cleanup. template TCircularDoublyLinkedList::~TCircularDoublyLinkedList() { // Before the parent destructors run, we must break the circular link. // The parent's `nullptr`-terminated cleanup loop will now work correctly. if (!this->IsEmpty()) { // The tail is the node before the dummy head. // Set its 'next' pointer to null instead of back to the head. this->head->GetPrev()->SetNext(nullptr); } // Now, the chain is broken: head -> node1 -> ... -> tail -> nullptr // The TDoublyLinkedList and TSingleLinkedList destructors will be called // automatically after this, and they will now function correctly. } // Moves the cursor to the first element in the list. template void TCircularDoublyLinkedList::ResetCursor() { if (this->IsEmpty()) { this->cursor = this->head; } else { this->cursor = this->head->GetNext(); } } // Returns the data at the cursor's current position. template T TCircularDoublyLinkedList::GetCursorData() const { // The cursor is only valid if it's not pointing to the dummy head. if (!this->IsEmpty() && this->cursor != this->head) { return this->cursor->GetData(); } return nullptr; } // Moves the cursor forward, wrapping around if needed. template void TCircularDoublyLinkedList::AdvanceCursor(int aSteps) { if (this->IsEmpty()) { return; } for (int i = 0; i < aSteps; ++i) { this->cursor = this->cursor->GetNext(); // If we move to the dummy head, we've wrapped around. Skip over it to the first element. if (this->cursor == this->head) { this->cursor = this->head->GetNext(); } } } // Moves the cursor backward, wrapping around if needed. template void TCircularDoublyLinkedList::RewindCursor(int aSteps) { if (this->IsEmpty()) { return; } for (int i = 0; i < aSteps; ++i) { this->cursor = this->cursor->GetPrev(); // If we move to the dummy head, we've wrapped around. Skip over it to the last element. if (this->cursor == this->head) { this->cursor = this->head->GetPrev(); } } } // Gets the numerical index of the cursor's current position. template int TCircularDoublyLinkedList::GetCursorIndex() const { if (this->IsEmpty() || this->cursor == this->head) { return -1; } int index = 0; TNode* current = this->head->GetNext(); // Traverse the list safely, knowing it will eventually loop back to the head. while (current != this->head) { if (current == this->cursor) { return index; } current = current->GetNext(); index++; } return -1; // Should not be reached if cursor is valid. } // Appends a node to the end of the list (before the dummy head). // Use the InternAppend from TDoublyLinkedList for basic logic, then adjust prev pointers. template void TCircularDoublyLinkedList::Append(const T& aData) { // 1. Let the base class do ALL the standard work. TDoublyLinkedList::Append(aData); // 2. Now fix the circular links. if(!this->IsEmpty()) { this->head->SetPrev(this->tail); // Dummy head's prev points to new tail this->tail->SetNext(this->head); // New tail's next points to dummy head } } // Prepends a node to the beginning of the list (after the dummy head). // Use the Prepend from TDoublyLinkedList, this will handle all the logic. template void TCircularDoublyLinkedList::Prepend(const T& aData) { TDoublyLinkedList::Prepend(aData); // No need to adjust circular links, as TDoublyLinkedList::Prepend already does it. } template void TCircularDoublyLinkedList::InternalRemoveNode(TNode* aNodeToDelete) { if (aNodeToDelete == nullptr || aNodeToDelete == this->head) return; // Invalid input // 1. Update cursor if it points to the node being deleted if (this->cursor == aNodeToDelete) { this->cursor = aNodeToDelete->GetNext(); } //2. Update tail if the last node is being removed if (aNodeToDelete == this->tail) { this->tail = aNodeToDelete->GetPrev(); } // 3. Relink neighbors (this maintains the circle automatically) TNode* prevNode = aNodeToDelete->GetPrev(); TNode* nextNode = aNodeToDelete->GetNext(); prevNode->SetNext(nextNode); nextNode->SetPrev(prevNode); // 4. Delete data if owned, then delete the node if (this->isDataOwner) { delete aNodeToDelete->GetData(); } delete aNodeToDelete; this->size--; // 5. If the list is now empty, ensure tail and cursor point to the head. if (this->IsEmpty()) { this->tail = this->head; this->cursor = this->head; } } // Removes the first node with the given value. template void TCircularDoublyLinkedList::Remove(const T& aData) { if (this->IsEmpty()) return; // Start searching from the first actual node TNode* current = this->head->GetNext(); // Traverse the entire circle once for (int i = 0; i < this->size; ++i) { if (current->GetData() == aData) { TNode* prevNode = current->GetPrev(); TNode* nextNode = current->GetNext(); // Update tail if the last node is being removed if (current == this->tail) { this->tail = prevNode; } // Relink neighbors (this maintains the circle automatically) prevNode->SetNext(nextNode); nextNode->SetPrev(prevNode); if (this->isDataOwner) { delete current->GetData(); } delete current; this->size--; return; } current = current->GetNext(); } } // Removes the last element from the list. template T TCircularDoublyLinkedList::RemoveLast() { if (this->IsEmpty()) { return nullptr; } TNode* nodeToRemove = this->tail; T dataToReturn = nodeToRemove->GetData(); TNode* newTail = nodeToRemove->GetPrev(); // Relink the new tail to the head to maintain the circle newTail->SetNext(this->head); this->head->SetPrev(newTail); this->tail = newTail; // Update tail pointer delete nodeToRemove; this->size--; if (this->IsEmpty()) { this->tail = this->head; // Reset tail if list is now empty } return dataToReturn; } // Removes all occurrences of a given value using a safe, bounded loop. template void TCircularDoublyLinkedList::RemoveAll(const T& aData) { if (this->IsEmpty()) return; TNode* current = this->head->GetNext(); int initialSize = this->size; // Loop based on the original size for (int i = 0; i < initialSize; ++i) { TNode* nextNode = current->GetNext(); // Get next node before potential deletion if (current->GetData() == aData) { TNode* prevNode = current->GetPrev(); if (current == this->tail) { this->tail = prevNode; } prevNode->SetNext(nextNode); nextNode->SetPrev(prevNode); if (this->isDataOwner) { delete current->GetData(); } delete current; this->size--; } current = nextNode; } if (this->IsEmpty()) { this->tail = this->head; } } // Reverses the entire list by swapping the next/prev pointers of every node. template void TCircularDoublyLinkedList::Reverse() { if (this->size <= 1) return; // Nothing to reverse // 1. Keep track of original head and tail nodes. TNode* newFirstNode = this->tail; // 2. Iterate through all nodes, swapping next and prev pointers. TNode* current = this->head; // We must loop size + 1 times to include the dummy head in the pointer swap. for (int i = 0; i < this->size + 1; ++i) { current->SwapNextPrev(); // The *new* prev pointer is the *original* next pointer, so this moves us forward. current = current->GetPrev(); } // 3. Correct the head and tail pointers. this->tail = this->head->GetNext(); // The old head is the new tail. this->head->SetNext(newFirstNode); // The dummy head now points to the old tail. newFirstNode->SetPrev(this->head); // New first node's prev points to dummy head. } // Reverses a portion of the list, ensuring circular links are maintained. template void TCircularDoublyLinkedList::ReverseSublist(int start, int end) { if (start < 0 || end >= this->size || start >= end) { return; } // --- 1. Find boundary nodes --- TNode* startNode = this->head->GetNext(); for (int i = 0; i < start; ++i) { startNode = startNode->GetNext(); } TNode* endNode = startNode; for (int i = start; i < end; ++i) { endNode = endNode->GetNext(); } TNode* startPrev = startNode->GetPrev(); TNode* endNext = endNode->GetNext(); // --- 2. Reverse pointers for all nodes within the sublist --- TNode* current = startNode; for (int i = 0; i <= (end - start); ++i) { TNode* temp = current->GetNext(); current->SetNext(current->GetPrev()); current->SetPrev(temp); current = temp; } // --- 3. Re-stitch the sublist (no nullptr checks needed) --- startPrev->SetNext(endNode); endNode->SetPrev(startPrev); startNode->SetNext(endNext); endNext->SetPrev(startNode); // Update tail pointer if it was part of the reversed segment if (this->head->GetPrev() == endNode) { // Original startNode is now at the end this->tail = startNode; } } // Merges another sorted list, creating a single, sorted circular list. template void TCircularDoublyLinkedList::Merge(TSingleLinkedList& otherList) { // We can call the parent implementation to do the heavy lifting of merging. TDoublyLinkedList::Merge(otherList); // The parent merge results in a null-terminated list. We just need to fix the ends. if (!this->IsEmpty()) { TNode* firstNode = this->head->GetNext(); this->tail->SetNext(firstNode); firstNode->SetPrev(this->tail); } } // Applies a function to each node using a safe loop for a circular list. template void TCircularDoublyLinkedList::ForEach(FVisitNode aVisitNode) const { if (aVisitNode == nullptr || this->IsEmpty()) { return; } TNode* current = this->head->GetNext(); int index = 0; while (current != this->head) { aVisitNode(current->GetData(), index); current = current->GetNext(); index++; } } // Checks if a value is in the list using a safe, non-infinite loop. template bool TCircularDoublyLinkedList::Contains(const T& aData) const { if (this->IsEmpty()) { return false; } TNode* current = this->head->GetNext(); while (current != this->head) { if (current->GetData() == aData) { return true; } current = current->GetNext(); } return false; } // Searches for a value using a safe, non-infinite loop. template T TCircularDoublyLinkedList::Search(const T& aData, FCheckNode aCheckNode) const { if (this->IsEmpty()) { return nullptr; } TNode* current = this->head->GetNext(); while (current != this->head) { if (aCheckNode != nullptr) { if (aCheckNode(current->GetData(), aData)) { return current->GetData(); } } else { if (current->GetData() == aData) { return current->GetData(); } } current = current->GetNext(); } return nullptr; } // Finds the middle node by traversing to the size/2 index. template TNode* TCircularDoublyLinkedList::GetMiddle() const { if (this->IsEmpty()) return nullptr; // The safest way for a list where we know the size. int middleIndex = this->size / 2; TNode* current = this->head->GetNext(); for (int i = 0; i < middleIndex; ++i) { current = current->GetNext(); } return current; } #endif // TCIRCULARDOUBLYLINKEDLISTTEMPLATE_HPP