Working with linked lists in C++ using iterators, push back, insert, remove, and erasing duplicates

Running

Source Code

main.cpp

#include <string>
#include <iostream>
#include <cassert>

using namespace std;

class List;
class Iterator;
class Node
{
  public:
    /*
    Constructs a node with a given data value.
    @param s the data to store in this node
    */
    Node(string s);
  private:
    string data;
    Node* previous;
    Node* next;
    friend class List;
    friend class Iterator;
};

class List
{
  public:
    /**
    Constructs an empty list.
    */
    List();
    /**
    Appends an element to the list.
    @param data the value to append
    */
    void push_back(string data);
    /**
    Inserts an element into the list.
    @param iter the position before which to insert
    @param s the value to append
    */
    void insert(Iterator iter, string s);
    /**
    Removes an element from the list.
    @param iter the position to remove
    @return an iterator pointing to the element after the
    erased element
    */
    Iterator erase(Iterator iter);
    /*
    removed all elements equal to the string data (passed-in value)
    */
    Iterator remove(string data);
    /*
    reverses the elements in the list
    */
    void reverse();
    /*
    removes repeats in list
    */
    void unique();
    /**
    Gets the beginning position of the list.
    @return an iterator pointing to the beginning of the list
    */
    Iterator begin();
    /**
    Gets the past-the-end position of the list.
    @return an iterator pointing past the end of the list
    */
    Iterator end();
  private:
    Node* first;
    Node* last;
    friend class Iterator;
};

class Iterator
{
  public:
  /**
  Constructs an iterator that does not point into any list.
  */
    Iterator();
    /**
    Looks up the value at a position.
    @return the value of the node to which the iterator points
    */
    string get() const;
    /**
    Advances the iterator to the next node.
    */
    void next();
    /**
    Moves the iterator to the previous node.
    */
    void previous();
    /**
    Compares two iterators.
    @param b the iterator to compare with this iterator
    @return true if this iterator and b are equal
    */
    bool equals(Iterator b) const;
  private:
    Node* position;
    List* container;
    friend class List;
};

Node::Node(string s)
{
  data = s;
  previous = NULL;
  next = NULL;
}

List::List()
{
  first = NULL;
  last = NULL;
}

void List::push_back(string data)
{
  Node* new_node = new Node(data);
  if (last == NULL) // List is empty
  {
    first = new_node;
    last = new_node;
  }
  else
  {
    new_node->previous = last;
    last->next = new_node;
    last = new_node;
  }
}

void List::insert(Iterator iter, string s)
{
  if (iter.position == NULL)
  {
    push_back(s);
    return;
  }

  Node* after = iter.position;
  Node* before = after->previous;
  Node* new_node = new Node(s);
  new_node->previous = before;
  new_node->next = after;
  after->previous = new_node;
  if (before == NULL) // Insert at beginning
    first = new_node;
  else
    before->next = new_node;
}

Iterator List::erase(Iterator iter)
{
  assert(iter.position != NULL);
  Node* remove = iter.position;
  Node* before = remove->previous;
  Node* after = remove->next;
  if (remove == first)
    first = after;
  else
    before->next = after;
  if (remove == last)
    last = before;
  else
    after->previous = before;
  delete remove;
  Iterator r;
  r.position = after;
  r.container = this;
  return r;
}

/*
//////////////////////////////////////////////
added/////////////////////////////////////////
//////////////////////////////////////////////
removes all instances of string data from the list
*/
Iterator List::remove(string data)
{
  for (Iterator pos = this->begin(); !pos.equals(this->end()); pos.next())
  {
    if (pos.get() == data)
    {
      this->erase(pos);
    }
  }
}

/*////////////////////////////////////////////
added/////////////////////////////////////////
//////////////////////////////////////////////
The way I approached this is I started by getting to the end of the linked list, and then I started adding the last
node's string value to the end of the list, every node's string value before it, one at a time, appending after the end.
So, in other words, ABCDEF would create a list ABCDEFFEDCBA.  I made a counter, and used that counter's ending value
to erase the first half of the list.  The first element does not append to the end in the for loop, and so I
added the extra push_back line to account for that.  The end result is the original list reversed.
*/
void List::reverse()
{
  Iterator pos;
 int counter = 0;
  for (pos = this->begin(); !pos.equals(this->end()); pos.next())
  {
    //do nothing
  }
  pos.previous();
  //cout << endl << pos.get();
  for (pos = pos; !pos.equals(begin()); pos.previous())
  {
    push_back(pos.get());
    counter++;
  }
  push_back(pos.get());  //appends the original list's first element to the end of the new modified (reversed) list
  for (int x = 0; x < counter + 1; x++)
  {
    erase(begin());
  }
}

/*////////////////////////////////////////////
added/////////////////////////////////////////
//////////////////////////////////////////////
I iterate through the linked list to count how many nodes there are and store that into integer counter.  I then
start an iterator at the beginning of the linked list and iterate through the counter, each time setting a second counter
to zero and checking if for the end of the list.  If the end is not yet here, I set the value to current and iterate to
the next node, where I compare all nodes from there to the end to that of the first value.  This process then repeats for
the second value (iterates through the 3rd to final value to compare it to 2nd, then from 4th to final to compare to 3rd,
etc.)  So in a list ABCDEF, A would be checked for repeats through BCDEF, then B would be checked for repeats fhrough CDEF,
then C through DEF, etc.  The end result is a list with no repeats.
*/
void List::unique()
{
  string current;
  int counter = 0;
  int counter2 = 0;
  Iterator pos;
  for (pos = this->begin(); !pos.equals(this->end()); pos.next())
  {
    counter++;
  }
  pos = begin();
  for (int x = 0; x < counter - 1; x++)
  {
     counter2 = 0;
     if (!pos.equals(this->end()))
     {
       current = pos.get();
       pos.next();
       for (pos = pos; !pos.equals(this->end()); pos.next())
       {
         counter2++;
         if (current == pos.get())
           erase(pos); 
       }
     }
     for (counter2 = counter2; counter2 > 0; counter2--)
     {
       pos.previous();
     }
  }
}

Iterator List::begin()
{
  Iterator iter;
  iter.position = first;
  iter.container = this;
  return iter;
}

Iterator List::end()
{
  Iterator iter;
  iter.position = NULL;
  iter.container = this;
  return iter;
}

Iterator::Iterator()
{
  position = NULL;
  container = NULL;
}

string Iterator::get() const
{
  assert(position != NULL);
  return position->data;
}

void Iterator::next()
{
  assert(position != NULL);
  position = position->next;
}

void Iterator::previous()
{
  assert(position != container->first);
  if (position == NULL)
    position = container->last;
  else
    position = position->previous;
}

bool Iterator::equals(Iterator b) const
{
  return position == b.position;
}

/*
The soul purpose of this method is to reduce redundancy.  This prints out the entire List to the screen.
*/
void printList(List staff)
{
  Iterator pos;
  for (pos = staff.begin(); !pos.equals(staff.end()); pos.next())
    cout << pos.get() << endl;  
}

int main()
{
  List staff;
  staff.push_back("Tom");
  staff.push_back("Dick");
  staff.push_back("Harry");
  staff.push_back("Juliet");
  staff.push_back("Mark");
  staff.push_back("Juliet");   //<--repeat
  staff.push_back("Juliet");   //<--repeat
  staff.push_back("John");
  staff.push_back("Fred");
  staff.push_back("Ron");
  staff.push_back("John");     //<--repeat
  staff.push_back("Jason");
  staff.push_back("Michael");
  staff.push_back("Tom");      //<--repeat
  staff.push_back("Fred");     //<--repeat
  staff.push_back("Robin");
  
  
  cout << "Original list:\n\n";
  printList(staff);
  
  cout << "\n\n'Fred' removed from the list:\n\n";
  staff.remove("Fred");  //removes all instances of "Fred" from the list
  printList(staff);

  cout << "\n\nList reversed:\n\n";
  staff.reverse();
  printList(staff);

  cout << "\n\nAll repeats removed from list:\n\n";
  staff.unique();
  printList(staff);
 
  return 0;
}