Lists

    Reading: Dietel section 15.4

Sequence ADT

Implementation as list template class
List Class

    template < class T > class ListNode

    {

        template < class TT > friend class List ;   // List can access ListNode's private data / methods

                                                                      // Can't have a List without ListNodes

        public:

                ListNode ( const T& ) ;  // constructor; needs T object

        private:

                T data;  // the stored object

                ListNode < T > *  next_ptr; // the next object

                ListNode < T > *  prev_ptr;  // the previous object

    } ;

    template < class T>
    ListNode < T > :: ListNode ( const T& t )
    {
        data = t;
        next_ptr = prev_ptr = 0;    // Null pointers; List class will set these appropriately
    }

template < class T >
class List
{
    public:
                List ( ) ;  // constructor
                ~List ( ) ; // destructor
                List ( const List < T > & );  // copy constructor
                List < T > & operator= ( const List < T > &);
                bool Contains ( const T& ) const;  // Is this object in the list?
                bool IsEmpty ( ) const;
                int NumElements ( ) const;
                void Print ( ostream& = cout ) const;  // we'll also write a << operator

                void InsertAfter ( iterator position, const T& new_value );
                void InsertBefore ( iterator position, const T& new_value );
                void InsertAtFront ( const T& new_value );
                void InsertAtBack ( const T& new_value );
                void Delete ( iterator position );  // Deletes item pointed to by iterator

                T front ( );  // Value of first element
                T back ( );  // Value of last element

                class iterator
                    {
                        public:
                                    iterator ( );
                                    T& operator* ( ) const;
                                    iterator& operator ++ ( );  // Prefix operator
                                    iterator& operator -- ( );   //  Prefix operator
                                    bool operator == ( const iterator& ) const;
                                    bool operator != ( const iterator& ) const;
                                    friend class List;

                        private:
                                    ListNode < T > * current;
                                    iterator ( ListNode < T > * );  // Private constructor; use in begin function
                    } ;
 

                iterator begin ( );  // Points to first element
                iterator end ( );  // Points to one-past-last element

                // begin, end functions are not const; they will allow changes to be made to the List

    private:
                ListNode < T > * head;  // first element
                ListNode < T > * tail;     // one past last valid element
                int num_elements;

                void Copy ( const List < T > & ) ;  // To be used in copy constructor, assignment operator

                void Destroy ( ) ; // To be used in destructor, assignment operator

} ;

template < class T >
List < T > :: iterator :: iterator ( )    // Left-to-right :: associativity
{
    current = 0;
}

template < class T >
T& List < T> :: iterator :: operator* ( )
{
    if ( current -> next_ptr ) // Don't access dummy node's data

        return current -> data;  // Throw an exception if no current element

}

template < class T >
iterator& List < T > :: iterator  :: operator++ ( )
{

    if ( current -> next_ptr == 0 )

        // Throw some exception

    current = current -> next_ptr;

    return *this;

}

template < class T >
iterator& List < T > :: iterator  :: operator -- ( )
{

    if ( current -> prev_ptr == 0 )

        // Throw some exception

    current = current -> prev_ptr;

    return *this;

}

 

template < class T >
bool  List < T > :: iterator  :: operator == ( const List < T > :: iterator& i ) const
{
    return ( current == i . current );

}

template < class T >
bool List < T> :: iterator :: operator != ( const List < T > :: iterator& i ) const
{
    return ! ( *this == i );
}
 

template < class T >

List < T > :: iterator :: iterator ( ListNode < T > * nodeptr)  // private constructor

{  current = nodeptr; }

template < class T >
List < T > :: List ( )
{
    num_elements = 0;
    head = tail = new ListNode < T > ( T ( ) );  // Dummy node; represents "one past end"

}

template < class T >
List < T > :: Destroy ( )  // Deallocate all list elements
{
    ListNode < T > * next;
    ListNode < T > * curr = head;
    while ( curr )  // not null pointer
        {
            next = curr -> next_ptr;
            delete curr;
            curr = next;
        }
}

template < class T > 

List < T > :: ~List ( ) // Destructor

{

    Destroy ( );

}

 

template < class T >
List < T > :: iterator List < T > :: begin ( )
{

   return iterator ( head );  // Use of private iterator constructor

}

template < class T >
List < T > :: iterator List < T > :: end ( )
{
    return iterator ( tail );  // Points to dummy node

}
 

template < class T >

void List < T > :: Copy ( const List < T >& rhs ) 

{

num_elements = rhs.num_elements;

head = tail = new ListNode < T > ( T ( ) ); // Assumes T default constructible

iterator i = rhs.begin ( );

ListNode < T > * curr;

ListNode < T > * next;

if ( i != rhs.end ( ) )

    {

    head = new ListNode < T > ( * i );  // ListNode's copy constructor

    head -> next_ptr = tail;  // no next element besides dummy (yet)

    tail -> prev_ptr = head;

    ++ i;  // move to next element

    curr = newlist -> head;

    }

while ( i != rhs.end ( ) )  // while there are still more elements to be copied

{

    curr -> next_ptr = new ListNode < T > ( *i );        // copy the next element from rhs

    ( curr -> next_ptr ) -> prev_ptr = curr;

    curr = curr -> next_ptr;

    ++ i;

}

curr -> next_ptr = tail;  // handle last node separately; points to dummy tail node

tail -> prev_ptr = curr;

}

template < class T >
List < T > :: List ( const List < T > & rhs ) // copy constructor
{
    Copy ( rhs );

}

template < class T >

List < T >& List < T > :: operator= ( const List < T > & rhs )

{

    if ( this != &rhs )

    {

        Destroy ( );  // Deallocate all current list elements

        Copy ( rhs );

      }

    return *this;

}

template < class T >

bool  List < T > :: Contains ( const T& value ) const

{

    ListNode < T > * curr = head;

    while ( curr -> next_ptr != 0 )  // Don't check the dummy node's value

    {

        if ( curr -> data == value )  // uses T's equality operator

            return true;

        curr = curr -> next_ptr;

    }

    return false;

}
 

template < class T >

bool List < T > :: IsEmpty ( ) const

{  return  ( num_elements == 0 );  }

template < class T >

int List < T > :: NumElements ( ) const

{  return num_elements; }

template < class T >

void List < T > :: Print ( ostream& o ) const

{

  ListNode < T > * curr = head ;

  while ( curr -> next_ptr != 0 )  // Don't print dummy node's value

    {

        o << curr -> data << " ";  // assumes << operator for T type

        curr = curr -> next_ptr;

    }

}

template < class T >
ostream& operator << ( ostream& o, const List < T >& L)
{
    List < T > :: iterator i = L.begin ( );
    while ( i != L.end ( ) )
        o << *i << endl;

    return o;
}

template < class T >
T List < T > :: front ( ) const
{

    if ( num_elements )
        return head -> data;   // Throw an exception if list empty
}

template < class T >
T List < T > :: back ( ) const
{

    if ( num_elements )
     return tail -> prev_ptr -> data;        // Throw an exception if list empty
}

template < class T >

void List < T> :: InsertAfter ( iterator position, const T& value )

{  // Assumes iterator points to an element in the lhs list

    if ( i == end ( ) )

        // Throw some exception; can't insert after the dummy node

    ListNode < T > * node_before_new = position . current;

    ListNode < T > * node_after_new = node_before_new -> next_ptr;

    ListNode < T > * new_node = new ListNode < T > ( value );  // ListNode constructor

    node_before_new -> next_ptr = new_node;

    new_node -> prev_ptr = node_before_new;

    new_node -> next_ptr = node_after_new;  

    node_after_new -> prev_ptr = new_node;

    num_elements++;  // one more element now

}

template < class T >
void InsertBefore ( iterator position, const T& value )
{  // assumes iterator points to an element of the lhs List
    if ( position != head ) // Not inserting before the first element

        InsertAfter ( -- position, value );

    else

        {

            ListNode < T > * new_node = new ListNode < T > ( value );

            new_node -> next_ptr = head;

            head -> prev_ptr = new_node;

            head = new_node;

            num_elements ++;

        }

}

 
template < class T >

void List < T > :: InsertAtFront ( const T& value )

{

    InsertBefore ( begin ( ), value );

}   

template < class T >

void List < T > :: InsertAtBack ( const T& value )

{

   InsertBefore ( end ( ), value );

}

template < class T >

void List < T > :: Delete ( iterator position )
{      // Assumes iterator points to an element of the lhs List


    if ( position . current == head )
        {
            head = head -> next_ptr;
            head -> prev_ptr = 0;

            delete position . current;
        }

    else if ( position . current  == tail )
                // Throw some exception; can't delete dummy node

    else
        {
            ListNode < T > * node_before = ( position . current ) -> prev_ptr;
            ListNode < T > * node_after = ( position . current ) -> next_ptr;

            node_before -> next_ptr = node_after;
            node_after -> prev_ptr = node_before;

            delete position . current;
          }

    num_elements --;
}

List Usage Examples

List < int > IntList;

IntList . InsertAtFront ( 8 );  // IntList is now: 8

IntList . InsertAtBack ( 5 );  // IntList is now: 8 5

List < int > :: iterator i = IntList . begin ( );

IntList . InsertAfter ( i , 3);   // IntList is now: 8 3 5

for (  ; i != IntList.end( ); ++i )

    *i *= 2;        // After loop, IntList is: 16 6 10

 

List < Polynomial > PolyList;        

for ( int i = 0; i < 5 ; i++ )

    PolyList . InsertAtFront ( Polynomial ( 4, CoefficientList [ i ] );  // CoefficientList is 2-D array of doubles

List < Polynomial > :: iterator p = PolyList . begin ( );

while ( p != PolyList . end ( ) )

    {

        cout << i << "th polynomial: " << *p << endl;  // This would use Polynomial's << operator

        ++ p;

    }

PolyList . Delete ( PolyList.begin ( ) ) ; // Removes first element

PolyList . Delete ( -- ( PolyList . end ( ) ) ) ; // Removes last element