adding ds list code
This commit is contained in:
245
lectures/11_list_implementation/dslist_handout.h
Normal file
245
lectures/11_list_implementation/dslist_handout.h
Normal file
@@ -0,0 +1,245 @@
|
||||
#ifndef dslist_h_
|
||||
#define dslist_h_
|
||||
// A simplified implementation of a generic list container class,
|
||||
// including the iterator, but not the const_iterators. Three
|
||||
// separate classes are defined: a Node class, an iterator class, and
|
||||
// the actual list class. The underlying list is doubly-linked, but
|
||||
// there is no dummy head node and the list is not circular.
|
||||
#include <cassert>
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// NODE CLASS
|
||||
template <class T>
|
||||
class Node {
|
||||
public:
|
||||
Node() : next_(NULL), prev_(NULL) {}
|
||||
Node(const T& v) : value_(v), next_(NULL), prev_(NULL) {}
|
||||
|
||||
// REPRESENTATION
|
||||
T value_;
|
||||
Node<T>* next_;
|
||||
Node<T>* prev_;
|
||||
};
|
||||
|
||||
// A "forward declaration" of this class is needed
|
||||
template <class T> class dslist;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST ITERATOR
|
||||
template <class T>
|
||||
class list_iterator {
|
||||
public:
|
||||
// default constructor, copy constructor, assignment operator, & destructor
|
||||
list_iterator() : ptr_(NULL) {}
|
||||
list_iterator(Node<T>* p) : ptr_(p) {}
|
||||
list_iterator(const list_iterator<T>& old) : ptr_(old.ptr_) {}
|
||||
list_iterator<T>& operator=(const list_iterator<T>& old) {
|
||||
ptr_ = old.ptr_; return *this; }
|
||||
~list_iterator() {}
|
||||
|
||||
// dereferencing operator gives access to the value at the pointer
|
||||
T& operator*() { return ptr_->value_; }
|
||||
|
||||
// increment & decrement operators
|
||||
list_iterator<T>& operator++() { // pre-increment, e.g., ++iter
|
||||
ptr_ = ptr_->next_;
|
||||
return *this;
|
||||
}
|
||||
list_iterator<T> operator++(int) { // post-increment, e.g., iter++
|
||||
list_iterator<T> temp(*this);
|
||||
ptr_ = ptr_->next_;
|
||||
return temp;
|
||||
}
|
||||
list_iterator<T>& operator--() { // pre-decrement, e.g., --iter
|
||||
ptr_ = ptr_->prev_;
|
||||
return *this;
|
||||
}
|
||||
list_iterator<T> operator--(int) { // post-decrement, e.g., iter--
|
||||
list_iterator<T> temp(*this);
|
||||
ptr_ = ptr_->prev_;
|
||||
return temp;
|
||||
}
|
||||
// the dslist class needs access to the private ptr_ member variable
|
||||
friend class dslist<T>;
|
||||
|
||||
// Comparions operators are straightforward
|
||||
bool operator==(const list_iterator<T>& r) const {
|
||||
return ptr_ == r.ptr_; }
|
||||
bool operator!=(const list_iterator<T>& r) const {
|
||||
return ptr_ != r.ptr_; }
|
||||
|
||||
private:
|
||||
// REPRESENTATION
|
||||
Node<T>* ptr_; // ptr to node in the list
|
||||
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST CLASS DECLARATION
|
||||
// Note that it explicitly maintains the size of the list.
|
||||
template <class T>
|
||||
class dslist {
|
||||
public:
|
||||
// default constructor, copy constructor, assignment operator, & destructor
|
||||
dslist() : head_(NULL), tail_(NULL), size_(0) {}
|
||||
dslist(const dslist<T>& old) { this->copy_list(old); }
|
||||
dslist& operator= (const dslist<T>& old);
|
||||
~dslist() { this->destroy_list(); }
|
||||
|
||||
// simple accessors & modifiers
|
||||
unsigned int size() const { return size_; }
|
||||
bool empty() const { return head_ == NULL; }
|
||||
void clear() { this->destroy_list(); }
|
||||
|
||||
// read/write access to contents
|
||||
const T& front() const { return head_->value_; }
|
||||
T& front() { return head_->value_; }
|
||||
const T& back() const { return tail_->value_; }
|
||||
T& back() { return tail_->value_; }
|
||||
|
||||
// modify the linked list structure
|
||||
void push_front(const T& v);
|
||||
void pop_front();
|
||||
void push_back(const T& v);
|
||||
void pop_back();
|
||||
|
||||
typedef list_iterator<T> iterator;
|
||||
iterator erase(iterator itr);
|
||||
iterator insert(iterator itr, const T& v);
|
||||
iterator begin() { return iterator(head_); }
|
||||
iterator end() { return iterator(NULL); }
|
||||
|
||||
private:
|
||||
// private helper functions
|
||||
void copy_list(const dslist<T>& old);
|
||||
void destroy_list();
|
||||
|
||||
//REPRESENTATION
|
||||
Node<T>* head_;
|
||||
Node<T>* tail_;
|
||||
unsigned int size_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST CLASS IMPLEMENTATION
|
||||
template <class T>
|
||||
dslist<T>& dslist<T>::operator= (const dslist<T>& old) {
|
||||
// check for self-assignment
|
||||
if (&old != this) {
|
||||
this->destroy_list();
|
||||
this->copy_list(old);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::push_front(const T& v) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::pop_front() {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::push_back(const T& v) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::pop_back() {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// do these lists look the same (length & contents)?
|
||||
template <class T>
|
||||
bool operator== (dslist<T>& left, dslist<T>& right) {
|
||||
if (left.size() != right.size()) return false;
|
||||
typename dslist<T>::iterator left_itr = left.begin();
|
||||
typename dslist<T>::iterator right_itr = right.begin();
|
||||
// walk over both lists, looking for a mismatched value
|
||||
while (left_itr != left.end()) {
|
||||
if (*left_itr != *right_itr) return false;
|
||||
left_itr++; right_itr++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <class T>
|
||||
bool operator!= (dslist<T>& left, dslist<T>& right){ return !(left==right); }
|
||||
template <class T>
|
||||
typename dslist<T>::iterator dslist<T>::erase(iterator itr) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename dslist<T>::iterator dslist<T>::insert(iterator itr, const T& v) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::copy_list(const dslist<T>& old) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::destroy_list() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
206
lectures/11_list_implementation/list.h
Normal file
206
lectures/11_list_implementation/list.h
Normal file
@@ -0,0 +1,206 @@
|
||||
template <class T>
|
||||
class Node {
|
||||
public:
|
||||
T value;
|
||||
Node* next;
|
||||
Node* prev;
|
||||
private:
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
friend class dslist<T>;
|
||||
private:
|
||||
Node<T>* ptr;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class dslist {
|
||||
public:
|
||||
typedef list_iterator<T> iterator;
|
||||
typedef unsigned int size_type;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
size_type 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;
|
||||
itr2++;
|
||||
while (itr2 != this->end()) {
|
||||
*itr = *itr2;
|
||||
itr++;
|
||||
itr2++;
|
||||
}
|
||||
this->pop_back();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
83
lectures/11_list_implementation/list_main.cpp
Normal file
83
lectures/11_list_implementation/list_main.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <iostream>
|
||||
//#include <list>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
int main(){
|
||||
|
||||
// dslist is a template, not a type.
|
||||
dslist<std::string> teams;
|
||||
teams.push_back("rpi");
|
||||
teams.push_back("wpi");
|
||||
teams.push_back("yale");
|
||||
teams.push_back("brown");
|
||||
teams.push_back("cornell");
|
||||
teams.push_back("colgate");
|
||||
teams.push_back("miami");
|
||||
teams.push_back("colorado");
|
||||
teams.push_back("harvard");
|
||||
|
||||
// we can use a type alias defined inside a class even if there is no object of that class. The type alias becomes part of the class's scope and can be used anywhere in your code where the class's scope is visible. although no object of dslist is created in this code, the type alias size_type is still accessible because it is part of the class's scope.
|
||||
dslist<std::string>::iterator itr;
|
||||
for(itr = teams.begin(); itr != teams.end(); itr++){
|
||||
std::cout << *itr << std::endl;
|
||||
}
|
||||
std::cout<<"==============="<<std::endl;
|
||||
|
||||
teams.pop_back();
|
||||
|
||||
for(itr = teams.begin(); itr != teams.end(); itr++){
|
||||
std::cout << *itr << std::endl;
|
||||
}
|
||||
std::cout<<"==============="<<std::endl;
|
||||
|
||||
itr = teams.begin();
|
||||
itr++;
|
||||
teams.erase(itr);
|
||||
|
||||
for(itr = teams.begin(); itr != teams.end(); itr++){
|
||||
std::cout << *itr << std::endl;
|
||||
}
|
||||
std::cout<<"==============="<<std::endl;
|
||||
|
||||
teams.push_front("harvard");
|
||||
teams.push_front("princeton");
|
||||
|
||||
for(itr = teams.begin(); itr != teams.end(); itr++){
|
||||
std::cout << *itr << std::endl;
|
||||
}
|
||||
std::cout<<"==============="<<std::endl;
|
||||
|
||||
teams.pop_front();
|
||||
for(itr = teams.begin(); itr != teams.end(); itr++){
|
||||
std::cout << *itr << std::endl;
|
||||
}
|
||||
std::cout<<"==============="<<std::endl;
|
||||
|
||||
dslist<double> ll;
|
||||
ll.push_back(2.5);
|
||||
ll.push_back(3.4);
|
||||
int i = 0;
|
||||
dslist<double>::iterator itr2 = ll.begin();
|
||||
for (i=0, itr2 = ll.begin(); itr2 != ll.end(); itr2++, i++){
|
||||
std::cout << "ll[" << i << "] is " << *itr2 << std::endl;
|
||||
}
|
||||
|
||||
// copy a list - calls copy constructor.
|
||||
dslist<double> u(ll);
|
||||
itr2 = u.begin();
|
||||
itr2++;
|
||||
u.insert(itr2, 6.5);
|
||||
u.insert(itr2, 4.8);
|
||||
for (i=0, itr2 = u.begin(); itr2 != u.end(); itr2++, i++){
|
||||
std::cout << "u[" << i << "] is " << *itr2 << std::endl;
|
||||
}
|
||||
|
||||
// equivalent to list<double> w(v), w is a copy of the elements in v.
|
||||
// we use the const keyword in front of a variable definition to indicate that the value of the variable cannot be changed after it is initialized.
|
||||
dslist<double> w = ll;
|
||||
for (i=0, itr2 = w.begin(); itr2 != w.end(); itr2++, i++){
|
||||
std::cout << "w[" << i << "] is " << *itr2 << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user