Files
CSCI-1200/lectures/11_list_implementation/list.h
2024-02-13 13:20:33 -05:00

206 lines
4.2 KiB
C++

template <class T>
class Node {
// this is an exception in this course
// where we make the member variables of this tiny class public.
public:
T value;
Node* next;
Node* prev;
};
// A "forward declaration" of this class is needed
template <class T> class dslist;
template <class T>
class list_iterator {
public:
// default constructor
list_iterator(){
ptr = NULL;
}
list_iterator(Node<T>* p) {
ptr = p;
}
// assignment constructor
list_iterator& operator=(const list_iterator& other){
this->ptr = other.ptr;
return (*this);
}
// derefencing operator
T& operator*(){
return (ptr->value);
}
// pre-increment
list_iterator& operator++(){
this->ptr = this->ptr->next;
return (*this);
}
// post-increment
list_iterator operator++(int){
list_iterator temp = (*this);
this->ptr = this->ptr->next;
return temp;
}
// operator ==
bool operator==(const list_iterator& other){
return (this->ptr == other.ptr);
}
// operator !=
bool operator!=(const list_iterator& other){
return (this->ptr != other.ptr);
}
// the dslist class needs access to the private ptr_ member variable
friend class dslist<T>;
private:
Node<T>* ptr;
};
template <class T>
class dslist {
public:
typedef list_iterator<T> iterator;
// default constructor
dslist(){
head = nullptr;
tail = nullptr;
size_ = 0;
}
// copy constructor
dslist(const dslist<T>& other){
// here we must initialize head and tail, because we will use them in push_back().
head = nullptr;
tail = nullptr;
size_ = other.size_;
iterator itr = other.begin();
while(itr != other.end()){
this->push_back(*itr);
itr++;
}
}
// x=(w=v)
dslist<T>& operator=(const dslist<T>& other){
head = nullptr;
tail = nullptr;
size_ = other.size_;
iterator itr = other.begin();
while(itr != other.end()){
this->push_back(*itr);
itr++;
}
}
~dslist(){
iterator itr = this->begin();
while(itr != this->end()){
iterator itr2 = itr;
itr2++;
delete itr.ptr;
itr = itr2;
}
}
unsigned int size(){
return size_;
}
void push_back(const T& value){
Node<T> *temp = new Node<T>;
temp->value = value;
temp->next = NULL;
temp->prev = tail;
// if the list was originally empty
if(head == nullptr){
head = temp;
tail = temp;
}else{
tail->next = temp;
tail = temp;
}
size_ = size_ + 1;
}
void pop_back();
// just like push_back(), it just takes a value as its parameter.
void push_front(const T& value){
Node<T> *temp = new Node<T>;
temp->value = value;
temp->next = head;
temp->prev = nullptr;
if(head == nullptr){
head = temp;
tail = temp;
}else{
head->prev = temp;
head = temp;
}
size_ = size_ + 1;
}
void pop_front();
/* iterator implementation */
void erase(iterator itr){
dslist<T>::iterator itr2(itr.ptr->next);
itr.ptr->prev->next = itr2.ptr;
itr2.ptr->prev = itr.ptr->prev;
delete itr.ptr;
size_ = size_ - 1;
}
void insert(iterator itr, const T& value){
Node<T>* temp = new Node<T>;
temp->value = value;
temp->next = itr.ptr->next;
temp->prev = itr.ptr;
itr.ptr->next = temp;
// if inserting after the last element
if(itr.ptr->next != nullptr){
itr.ptr->next->prev = temp;
}
}
iterator begin() const {
return list_iterator(head);
}
iterator end() const {
return list_iterator((Node<T>*)nullptr);
//return list_iterator((Node<T>*)NULL);
//return list_iterator(NULL);
}
private:
// pointing to head node and tail node.
Node<T>* head;
Node<T>* tail;
unsigned int size_; // current size
};
// TODO: handle the case where the list is empty, i.e., tail==nullptr and can't be dereferenced.
template <class T>
void dslist<T>::pop_back(){
Node<T> * temp = tail;
tail = tail->prev;
tail -> next = NULL;
// delete the original tail.
delete temp;
size_ = size_ - 1;
}
// TODO: handle the case where the list is empty, i.e., head==nullptr and can't be dereferenced.
template <class T>
void dslist<T>::pop_front(){
Node<T> * temp = head;
head = head->next;
head->prev = nullptr;
// delete the original head.
delete temp;
size_ = size_ - 1;
}