CS245 - Comp Sci II

Chapter 11 - Classes in the Abstract

OBJECTIVES


11.1 Abstract Data Types: Bears, Cats, and Dogs


An abstract data type(ADT) is defined by describing

1. the logical structure of the information represented by an instance of the type
2. the operations performed on the information

Aspect I: Structures

List - either empty or consists of a finite collection of elements, arranged so that each element has at most one element following it and at most one element preceding it

Stack - a LIFO list
Queue - a FIFO list
Tree - each element may have more than one sucessor
Graph - each element may have more than one sucessor and more than one predecessor
Rings - lists with first and last element linked
Bags - collections with no structure
Tables - each element of one collection is associated with a single element of another collection

Aspect II: Operations

List - create, insert, delete, find, destroy
Stack - create, push, pop, destroy

ADTs and OOP

11.2 The List ADT - Array Implementation



If all of the elements are of the same type, we have a homogeneous list, otherwise we have a heterogeneous list

Some general categories of ADT operations are:

Specific operations for List:

Declaring the List Class

List of ???? - use parameterized class

template <class T> 
   class TList 
   { }

where T is now a place-holder for any type:

void Update(T x);

Tlist<T>& operator= (const Tlist<T>& lst);

and we would reference the TList class by TList<int> or Tlist<Fraction>

example of a generic swap function:

template<class SWAP_TYPE> void Swap(SWAP_TYPE& a, SWAP_TYPE& b)
{
   SWAP_TYPE temp = a;
   a = b;
   b = temp;
}

Example of Array Implementation

//---------- ALISTS.H ----------

#ifndef ALISTS_H
#define ALISTS_H
#include <iostream.h>

//-------------------------- TList
// Declaration of an array implementation of list of values of parametrized type.
// All insertion and deletion is done at the current position in the list.

template <class T>class TList
{
   friend ostream& operator<< (ostream& os, TList<T> l);
   public:
       TList();					// Construct an empty list.
       TList(const TList<T>& l);		             // copy constructor
       ~TList();				            // destructor

       TList<T>& operator= (const TList<T>& l);	// assignment overload

       void Insert(T x);				// Insert x at current position
       void Remove();				// Delete the node at the current position

       void Head(); 				// Move current location to the head
       void Tail(); 				// Move current location to the tail
       TList<T>& operator++ (int);		// Move current to next position (postfix)
       TList<T>& operator-- (int);		// Move current to prior position (postfix)

       T Retrieve() const;			// Return the element at the current position
       void Update(T x);			// Store x in current location

       int Length();				// Return the size of the list
    private:
       T	*value;				// pointer to the array
        int	size,				// number of elements in the list (<= max)
              max,			// size of the array
              current;		// current index in the array (0 <=# current < size)
       void Grow();				// Grow the array to twice its size
};
#endif

Defining the List Class

//---------- ALISTS.CPP ----------
#include <stdlib.h>		// for exit
#include "ALISTS.H"

//----------------- Definition of TList member functions and friends

template <class T> ostream& operator<< (ostream& os, TList<T> l)
// Display an entire TList, from first position to last.
{
    for (int i = 0; i < size; i++)
    os << value[i] << '\t';
    return os;
}
template<class T> TList<T>::TList()
// Default constructor.  Builds an empty list, using an array of 10 elements.
{	
    size = 0;
    max = 10;
    current = 0;
    value = new T[max]; 
}

template<class T> TList::TList(const TList& l)
// copy constructor
{
    size = l.size;
    max = l.max;
    current = l.current;

    value = new T[max];
    for (int i = 0; i < size; i++)
       value[i] = l.value[i];	
}

template<class T> TList::~TList()
// Delete all nodes in the List.
{
   delete []value;	 // We need the brackets because T might have its own destructor.
   size = 0;
   max = 0;
   current = 0;
}

template<class T> TList& TList::operator= (const TList& l)
// assignment overload
{
   size = l.size;
   max = l.max;
   current = l.current;

// Kill the old array and construct a new one, filling it with l's values
   delete []value;
   value = new T[max];
   for (int i = 0; i < size; i++)
      value[i] = l.value[i];
   return *this;	
}

template<class T> void TList::Insert(T x)
// Insert x at the current position.
{
   if (size == max)	// No room in the array, so make it larger.
      Grow();
   for (int i = size - 1; i >= current; i--)
      value[i + 1] = value[i];
   value[current] = x;
   size++;
}

template<class T> void TList::Remove()
// Delete the node at the current position.
// If the list is empty, display a warning, but continue working.
{
    if (size > 0)
   {
       for (int i = current; i < size - 1; i++)
           value[i] = value[i + 1];
       size--;
   }
   else
        cerr << "Attempt to Remove an element from an empty list" << endl;
}

template<class T> void TList::Head()
// Move current location to the head.
{ current = 0;}

template<class T> void TList::Tail()
// Move current location to the tail.
{
    if (size > 0)		// We don't have to do anything if the list is empty.
       current = size - 1;
}

template<class T> TList& TList::operator++ (int)
// Move current location to next node (postfix operator).
// Do nothing if current is at the tail of the list.
{
    if (current < size - 1)
        current++;
    return *this;
}

template<class T> TList& TList::operator-- (int)
// Move current location to previous node (postfix operator).
// Do nothing if current is at the head of the list
{
    if (current > 0)
        current--;
    return *this;
}

template<class T> T TList::Retrieve() const	
// Return the element at the current position.
// Quit the program if the list is empty, since we can't return anything.
{
    if (size > 0)
       return value[current];
   else
   {
       cerr << "Cannot Return an element from an empty list";
       exit(1);
   }
}

template<class T> void TList::Update(T x)
// Store x in current location.
// Quit the program if the list is empty.
{
    if (size > 0)
       value[current] = x;
    else
    {
       cerr << "Cannot Update an element in an empty list";
       exit(1);
    }
}

template<class T> int TList::Length()
// Return the number of elements in the list.
{ return size;}

template<class T> void TList::Grow()
// PRIVATE.  Double the size of the array
{
    max *= 2;
    T* newValue = new T[max];
    for (int i = 0; i < size; i++)
        newValue[i] = value[i];
    delete []value;
    value = newValue;
}

11.3 The List ADT - Linked List Implementation

template <class T>
class Node
{ public:
     Node() {next = NULL;}
     data value;
     Node* next;
};

//---------- LLISTS.CPP ----------

#include <stdlib.h>		// for exit
#include "LLISTS.H"

//----------------- Definition of TList member functions and friends
template<class T> ostream& operator<< (ostream& os, TList<T> l)
// Display an entire TList, from first position to last.
{
    Node<T>* p = l.first;
    while (p)
    {
         os << p->value << '\t';
         p = p->next;
     }
     return os;
}

template<class T> TList<T>::TList()
// Default constructor.  Builds an empty list.
{	first = current = last = NULL; size = 0; }

template<class T> TList<T>::TList(const TList<T>& l)
// copy constructor
{
    Node<T>	*lptr = l.first,			// tracks through the argument list
    *nptr;			// tracks through this list

    if (lptr == NULL)		
    {	current = first = last = NULL; size = 0;}
    else
    {
//---- Construct the first node in the list.
         nptr = new Node<T>;
         nptr->value = lptr->value;
         if (lptr == l.current)
               current = nptr;
         first = nptr;
         lptr = lptr->next;

//---- Track through the remainder of the list, copying nodes.
        while (lptr)
        {
            nptr->next = new Node<T>;
            nptr = nptr->next;
            if (lptr == l.current)
                 current = nptr;
           nptr->value = lptr->value;
           lptr = lptr->next;
        }
        nptr->next = NULL;
        last = nptr;
        size = l.size;
   }	
}

template<class T> TList<T>::~TList()
// Delete all nodes in the List.
{
    Node<T>* nptr = first;
    current = first;
    while (current)
    {
         current = current->next;
         delete nptr;
         nptr = current;
    }
    first = current = last = NULL;
    size = 0;
}

template<class T> TList<T>& TList<T>::operator= (const TList<T>& l)
// assignment overload
{	
//--------- First, destroy the existing list.
    Node<T>* nptr = first;
    current = first;
    while (current)
    {
         current = current->next;
         delete nptr;
         nptr = current;
    }

//--------- Now build the new list
    Node<T>	*lptr = l.first;	// tracks through the argument list

    if (lptr == NULL)		
    {	current = first = last = NULL; size = 0;}
    else
    {
//---- Construct the first node in the list.
         nptr = new Node<T>;
         nptr->value = lptr->value;
         if (lptr == l.current)
                current = nptr;
         first = nptr;
         lptr = lptr->next;

//---- Track through the remainder of the list, copying nodes.
        while (lptr)
        {
             nptr->next = new Node<T>;
             nptr = nptr->next;
             if (lptr == l.current)
                 current = nptr;
             nptr->value = lptr->value;
             lptr = lptr->next;
         }
         nptr->next = NULL;
         last = nptr;
         size = l.size;
     }
     return *this;	
}

template<class T> void TList<T>::Insert(T x)
// Insert x at the current position.
{
    Node<T>* nptr = new Node<T>;
    if (current == NULL)				// List is empty.
    {
        nptr->value = x;
        nptr->next = NULL;
        current = first = last = nptr;
    }
    else						// List is nonempty.
    {
        nptr->value = current->value;
        nptr->next = current->next;
        current->value = x;
        current->next = nptr;
        if (current == last)
            last = nptr;
   }
   size++;
}

template<class T> void TList<T>::Remove()
// Delete the node at the current position.
{
           //You fill it in!!!
}

template<class T> void TList<T>::Head()
// Move current location to the head.
{ current = first;}

template<class T> void TList<T>::Tail()
// Move current location to the tail.
{ current = last;}

template<class T> TList<T>& TList<T>::operator++ (int)
// Move current location to next node (postfix operator).
// Do nothing if current is at the tail of the list.
{
    if (current != last)
        current = current->next;
    return *this;
}

template<class T> TList<T>& TList<T>::operator-- (int)
// Move current location to previous node (postfix operator).
// Do nothing if current is at the head of the list
{
    if (current != first)
    {
        Node<T>* nptr = first;
        while (nptr->next != current)
        nptr = nptr->next;
       current = nptr;
    }
    return *this;
}

template<class T> T TList<T>::Retrieve() const	
// Return the element at the current position.
{ 
    if (current)
        return current->value;
    else
    {
        cerr << "Attempted to Return an element from an empty list";
        exit(1);
    }
}


template<class T> void TList<T>::Update(T x)
// Store x in current location.
{ 
    if (current)
        current->value = x;
    else
    {
         cerr << "Attempted to Update an element in an empty list";
         exit(1);
     }
}

template<class T> int TList<T>::Length()
// Return the number of nodes in the list.
{ return size;}

Traversing a List

p = head;
while (p)
{
    process node;
    p = p -> next;
}

11.4 Comparing Implementation

11.5 Applications of Stacks

Definition The stack class is any sequence of elements S = (e1 e2 ... en) in which en is identified as the top element and intersections and deletions can only be made at the top. Insertions in a stack are called Push, and deletions from a stack are called Pop. The principal methods for the stack class are:
Init Done ReadIn
Display Save Empty
Push Pop Top
TStack* stackPtr;
class TStack
{public:
TStack()
// Used: To initialize the receiver stack.
// Pre:  None
// Post: Receiver stack is initialized and Size = 0
//   and Error = False and S = ()

~TStack 
// Used: To destroy the receiver stack.
// Pre:  Receiver is initialized.
// Post: Receiver stack is destroyed.

void Push (T element)
// Used: To add a new element onto the top of the stack.
// Pre:  S = (e[1] e[2] ... e[N]) and Size = N
// Post: N < MaxElements and Size = N+1 and S = (e[1] e[2] ... e[N] Eptr)
//   or N = MaxElements and Error = True

void Pop()
// Used: To remove the top element from the stack.
// Pre:  S = (e[1] e[2] ... e[N]) and Size = N
// Post: N = 0 and Error = True
//   or N > 0 and S = (e[1] e[2] ... e[N-1]) and Size = N-1

T Top()
// Used: To access the element at the top of the stack
// Pre:  S = (e[1] e[2] ... e[N]) and Size = N
// Post: N = 0 and Error = True and result = NIL
//   or N > 0 and result = e[N]
}

Run-Time Stacks and Procedure Activation

The run-time stack in a computer keeps an up-to-date record of all the active procedure calls for a program which is running, and in the order in which they have been activated.

Stack Machines

von Neumann machine -

stack machine

Application: Evaluating Polish Expressions

RPN - Reverse Polish Notation (Lukasiewicz) Definition A Polish expression is any series of arithmetic operands x,y,... and binary arithmetic operators op (typically +, -, *, and /) that can be formed using the following rules:

Rules for evaluation:

Algorithm:

11.6 Applications of Queues

Definition The queue class is any sequence of elements S = (e1 e2 ... en) in which e1 is identified as the front and en is identified as the rear element and insertions can only be made at the rear and deletions can only be made at the front. Insertions in a queue are called Enter and deletions from a queue are called Leave. The principal methods for the queue class are:

Init Done ReadIn
Display Save Empty
Enqueue Dequeue Front
Rear

TQueue* queuePtr;
class TQueue
{public:
TQueue
// Used: To initialize the receiver queue.
// Pre:  None
// Post: The receiver queue is intialized 

~TQueue
// Used: To destroy the receiver queue.
// Pre:  Receiver queue is initialized.
// Post: The receiver queue is destroyed.

void EnQueue(T element)
// Used: To add a new object to the rear of the receiver queue
// Pre:  Q = (e[1] ... e[N]) and Size = N
// Post: Size = MaxElements ^ Error = True 
or Size < MaxElements ^ Q = (e[1] ... e[N] Eptr) and Size = N + 1)

void DeQueue()
// Used: To remove an object from the front of the queue.
// Pre:  Q = (e[1] e[2] ... e[N]) and Size = N
// Post: Size = 0 and Error = True or N > 0 ^ Q = (e[2] ... e[N]) and Size = N-1)

void Empty

T Front()
// Used: To access the object at the front of the queue.
// Pre:  Q = (e[1] ... e[N]) and Size = N
// Post: Size = 0 and result = Nil and Error = True or Size > 0 and result = e[1]

T Rear();
// Used: To access the object at the rear of the queue.
// Pre:  Q = (e[1] ... e[N]) and Size = N
// Post: Size = 0 and result = Nil and Error = True or Size > 0 and result = e[N]
}

Exercises