165 lines
5.0 KiB
C++
165 lines
5.0 KiB
C++
|
#include "doubly-linked-list.h"
|
||
|
|
||
|
using namespace seq;
|
||
|
|
||
|
// return the size (number of items in the doubly linked list)
|
||
|
size_t DoublyLinkedList::size() const
|
||
|
{
|
||
|
size_t count = 0;
|
||
|
for(DoublyLinkedListNode* n = this->head; n != nullptr; n = n->get_next()) count++;
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
// return pointer to the node at position i, counting from 0
|
||
|
// for negative numbers, count from the tail (-1) backward (-2, -3, ...)
|
||
|
DoublyLinkedListNode* DoublyLinkedList::index(int i) const
|
||
|
{
|
||
|
DoublyLinkedListNode* n = this->head;
|
||
|
if(i > 0)
|
||
|
{
|
||
|
for(int k = 0; k < i; k++)
|
||
|
{
|
||
|
assert(n != nullptr);
|
||
|
n = n->get_next();
|
||
|
}
|
||
|
}
|
||
|
else /** in the doubly linked list, we can walk backward, so let us allow it for negative i **/
|
||
|
{
|
||
|
DoublyLinkedListNode* n = this->tail;
|
||
|
for(int k = -1; k > i; k--)
|
||
|
{
|
||
|
assert(n != nullptr);
|
||
|
n = n->get_prev();
|
||
|
}
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
// add an item at the end of the list
|
||
|
void DoublyLinkedList::push_back(const int& pushed_item)
|
||
|
{
|
||
|
DoublyLinkedListNode* new_node = new DoublyLinkedListNode;
|
||
|
new_node->set_item(pushed_item);
|
||
|
|
||
|
if(this->empty()) this->head = new_node;
|
||
|
else
|
||
|
{
|
||
|
this->tail->set_next(new_node);
|
||
|
new_node->set_prev(this->tail); /** attachment in the doubly linked list goes both ways **/
|
||
|
}
|
||
|
this->tail = new_node;
|
||
|
}
|
||
|
|
||
|
// add an item at the beginning of the list
|
||
|
void DoublyLinkedList::push_front(const int& pushed_item)
|
||
|
{
|
||
|
DoublyLinkedListNode* new_node = new DoublyLinkedListNode;
|
||
|
new_node->set_item(pushed_item);
|
||
|
|
||
|
if(this->empty()) this->tail = new_node;
|
||
|
else
|
||
|
{
|
||
|
new_node->set_next(this->head); // FIX BUG from the previous version, which was "new_node->set_next(this->head->get_next());"
|
||
|
this->head->set_prev(new_node); // FIX BUG from the previous version, which was "this->head->get_next()->set_prev(new_node);"
|
||
|
}
|
||
|
this->head = new_node;
|
||
|
}
|
||
|
|
||
|
// remove the head node and item
|
||
|
void DoublyLinkedList::pop_front()
|
||
|
{
|
||
|
if(this->empty()) return; // nothing there to remove
|
||
|
DoublyLinkedListNode* successor = this->head->get_next();
|
||
|
|
||
|
if(successor) successor->set_prev(nullptr); /** we must detach the previous head node from its successor **/
|
||
|
delete this->head;
|
||
|
this->head = successor; // successor of the previous head is the new head
|
||
|
if(this->head == nullptr) this->tail = nullptr; // catch special case: the list is now empty
|
||
|
}
|
||
|
|
||
|
// remove the tail node and item
|
||
|
/** note how this is much easier for the doubly than for the singly linked list; **
|
||
|
** we can simply take the "pop_front()" implementation and do everything symmetrically **/
|
||
|
void DoublyLinkedList::pop_back()
|
||
|
{
|
||
|
if(this->empty()) return; // nothing there to remove
|
||
|
DoublyLinkedListNode* predecessor = this->tail->get_prev();
|
||
|
|
||
|
if(predecessor) predecessor->set_next(nullptr);
|
||
|
delete this->tail;
|
||
|
this->tail = predecessor; /** predecessor of the previous tail is the new tail **/
|
||
|
if(this->tail == nullptr) this->head = nullptr; /** catch special case: the list is now empty **/
|
||
|
}
|
||
|
|
||
|
// insert an item at index i
|
||
|
// for negative numbers, count from the tail (-1) backward (-2, -3, ...)
|
||
|
void DoublyLinkedList::insert_at(int i, const int& inserted_item)
|
||
|
{
|
||
|
if(i == 0)
|
||
|
{
|
||
|
this->push_front(inserted_item);
|
||
|
return;
|
||
|
}
|
||
|
else if(i == -1)
|
||
|
{
|
||
|
this->push_back(inserted_item);
|
||
|
return;
|
||
|
}
|
||
|
DoublyLinkedListNode* predecessor = this->index(i-1);
|
||
|
this->insert_successor_to(predecessor, inserted_item);
|
||
|
}
|
||
|
|
||
|
// remove the item at index i
|
||
|
// for negative numbers, count from the tail (-1) backward (-2, -3, ...)
|
||
|
void DoublyLinkedList::erase_at(int i)
|
||
|
{
|
||
|
if(i == 0)
|
||
|
{
|
||
|
this->pop_front();
|
||
|
return;
|
||
|
}
|
||
|
else if(i == -1)
|
||
|
{
|
||
|
this->pop_back();
|
||
|
return;
|
||
|
}
|
||
|
DoublyLinkedListNode* erased_node = this->index(i);
|
||
|
this->erase_node(erased_node);
|
||
|
}
|
||
|
|
||
|
// insert an item after given node
|
||
|
void DoublyLinkedList::insert_successor_to(DoublyLinkedListNode* predecessor, const int& inserted_item)
|
||
|
{
|
||
|
DoublyLinkedListNode* new_node = new DoublyLinkedListNode;
|
||
|
new_node->set_item(inserted_item);
|
||
|
|
||
|
DoublyLinkedListNode* successor = predecessor->get_next();
|
||
|
predecessor->set_next(new_node);
|
||
|
new_node->set_prev(predecessor); /** attach both ways **/
|
||
|
new_node->set_next(successor);
|
||
|
|
||
|
if(!successor) this->tail = new_node;
|
||
|
else successor->set_prev(new_node); /** attach both ways **/
|
||
|
}
|
||
|
|
||
|
/** remove "erased_node" from the doubly linked list **/
|
||
|
void DoublyLinkedList::erase_node(DoublyLinkedListNode* erased_node)
|
||
|
{
|
||
|
if(erased_node->get_prev() == nullptr) /** erase the head **/
|
||
|
{
|
||
|
this->pop_front();
|
||
|
return;
|
||
|
}
|
||
|
if(erased_node->get_next() == nullptr) /** erase the tail **/
|
||
|
{
|
||
|
this->pop_back();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/** now attach predecessor and successor to each other **/
|
||
|
erased_node->get_prev()->set_next(erased_node->get_next());
|
||
|
erased_node->get_next()->set_prev(erased_node->get_prev());
|
||
|
|
||
|
delete erased_node; /** "erased_node" detached now, we can delete it **/
|
||
|
}
|