Homework 5: The List ADT
Singly Linked List Implementation
Overview
In Homework 4, you created an implementation of the List ADT using an array to store the list data items. Although this approach is intuitive, it frequently is inefficient in storage usage and always inefficient in operation time. It wastes memory by allocating an array that is large enough to store what you estimate to be the maximum number of data items a list will ever hold. In most cases, the list is rarely this large and the extra memory simply goes unused. In addition, the insertion and deletion operations require shifting data items back and forth within the array, a very time consuming task.
In this homework, you will implement the List ADT using a singly linked list. This implementation allocates memory data item by data item as items are added to the list. Equally important, a linked list can support insertion and deletion operations simply by changing a few links.
Note that we are not changing the interface of the List ADT. A good interface definition should not have to change when we change the underlying implementation. If it does, then some aspect of the implementation has been reflected through the interface definition, and we have failed in our goal of information hiding. Thus the abstract properties of a List still hold:
- It is homogenious -- the elements in the collection are all of the same type.
- It has a finite length (the number of elements).
- The elements are arranged sequentially:
- There is a first element and a last element
- Every element except the last has a unique successor
- Every element except the first has a unique predecessor
As we consider our linked list implementation, we see that, where the array implementation provided (by successive array elements) the sequential ordering of the list, we now use pointers explicitly linking list nodes together.
The next thing to consider is the data type of the data structures that are linked together. The list elements themselves have a common type abstracted by our Item typedef (or class). It would be poor practice to incorporate the links within this Item type, so we need to create a new type that acts as a container for the Item and also has the links required by our implementation. We will call this type a Node. A Node has two data members: an Item and a pointer to the next Node. For ease of manipulation by our List class, we will make these data members public. The Node class should simply have constructors in addition to the data members. We will discuss in class why we do not need a copy constructor nor an overload on the assignment operator.
Attributes/Domain
- As described above, the primary data item/attributes of a List object is a finite length homogeneous collection of data items arranged in sequential fashion.
- In addition, each list has the notion of a positional indicator. This is used as a kind of cursor to indicate some current item in the list. The position can be any of the following: {first item, second item, ..., last item, EOL}, where EOL indicates the "End Of List", and is logically a position beyond the last item in the list. The position must always be one of the above set. For an empty list, the position must be EOL.
- To aid in efficient implementation of insert and delete operations, the List class will maintain an internal attribute that tracks the predecessor of the current position. This position can logically be any of the following: {BOL, first item, second item, ..., last item}. On an empty list or when the current position is the first item, the predecessor position is BOL, indicating "Beginning Of List". When the current position is EOL, the predecessor is the last item. Across all operations on the List, the predecessor must, after the operation, have the correct predecessor value based on the value of the current position.
Operations
Subordinate Item Type
Item: We will use a type name of Item to denote the type of the elelments within a List. We can use a "type synonym" facility of C++ called typedef which will allow us to make the Item type a synonym for an existing type. For more complex types, we can define Item to be a struct which gathers together the data representing each instance of the type. Finally, if we want to include operations associated with the Item type, we can define Item to be a class encapsulating the type of the data items. For the first two styles of Item type definition, we can include these type declarations in the class definition file for the List class (i.e. in List.h). For the third, we would create a separate class definition file and implementation file for the Item class type.
Typedef example: typedef int Item; or typedef char Item;
Dependent Definitions
MAX_ITEMS: a named constant specifying the maximum number of items in a list.
Constructor/Destructor
List ( )
Preconditions: None.
Postconditions: Creates an empty list capable of holding Item type data items. Current position is at EOL. The List has a capacity of at most MAX_ITEMS items.List ( int max )
Preconditions: 0 <= max <= MAX_ITEMS.
Postconditions: Creates an empty list capable of holding Item type data items. Current position is at EOL. The List has a capacity of at most max items.List ( const List & src )
Preconditions: None
Postconditions: Creates a new List which is a deep copy of the passed src List. Implement the copy constructor last, after the rest of the implementation has been debugged.~List ( )
Preconditions: None.
Postconditions: Destroys the list, cleaning up all resources associated with the object.Mutator(s)
void insert ( const Item & item )
Preconditions: List is not full (i.e. the number of items currently in the list is less than its capacity).
Postconditions: The item has been inserted by copying into the list. Location of the insert occurs before the current position. After an insert, the current position follows the inserted item.void remove ( )
Preconditions: Current position is not EOL.
Postconditions: The item at the current position is removed from the list. The current position becomes that of the item's successor, or EOL if the removed item were the last in the list.void reset ( )
Preconditions: None.
Postconditions: Resets the current position to the first item in the list. If the list is empty, then the position is set to EOL.bool advance ( )
Preconditions: None.
Postconditions: Advances the current position to the next item in the list. Advancing from EOL leaves the current position at EOL. If the position following the advance is not EOL, the result of the function is TRUE, and if the position following the advance is EOL, the result of the function is FALSE.Observers (Predicates and Accessor(s))
bool isEmpty ( ) const
Preconditions: None.
Postconditions: Return value is true if the list contains no items, and false otherwise.bool atEOL ( ) const
Preconditions: None.
Postconditions: Return value is true if the current position is logically at EOL, and false otherwise.bool isFull ( ) const
Preconditions: None.
Postconditions: Return value is true if the list contains its capacity of items, and false otherwise.Item getCurrent ( ) const
Preconditions: Current position is not EOL.
Postconditions: Return value is a copy of the item at the current position. The list is unaffected.Print/String
void display ( ostream & stream ) const
Preconditions: Item type supports stream insertion to an output stream.
Postconditions: Outputs the contents of the list to the given stream. Form of the output must be strictly adhered to. The output uses '(' and ')' to denote the beginning and the end of the list. The output use space separation between elements of the list as well as between the beginning '(' and the first element, and the ending ')' and the last element. If the current position is not EOL, the element should be displayed between vertical bars ('|'). If the current position is EOL, this should be denoted by two vertical bars together at the end of the list. The list display should terminate with a newline. Note that this operation should not change the internal data associated with the list, and that includes the cursor position. Examples:
- Empty List -- ( || )
- One element list with cursor at EOL -- ( a || )
- One element list with cursor at that element -- ( |a| )
- Two element list with cursor at second element -- ( a |b| )
- Two element list with cursor at end -- ( a b || )
Assignment
Be sure and use the C++ Programming Style Guide and follow the conventions described there. Up to 25% of the grade for this homework will be based on following these conventions and practicing good documentation.
Your task is to implement the List ADT using a linked list, and in order to do so and to test, define the Item type, the Node class, and a driver program. To do this you will create a header file, named ListLink.h which contains the class definition, and an implementation file, named ListLink.cpp, which contains all of the definitions of the member functions corresponding to the operations described above. Likewise, you will create files Node.h and Node.cpp to wrap the Item elements and to realize the structures being linked together in the List.
You should use dynamic allocation at Item insertion, and should return to free space the allocated storage on a remove and (for all elements) at object destruction.
As before, the driver program should be general. It should allow the notion of two List objects, the "current" list and the "alternate" list. Single List object operations pertain to the "current" list. For the copy constructor and the assignment overload, the "current" list is the one that gets updated (is logically the lhs), and the "alternate" list is the one that is copied from (is logically the rhs). There is also a command to switch the roles of the two lists, making the current the alternate and the alternate the current.
Test List Driver Commands Command Action CcCreate a new List object (using the new C++ operator), making it the current List; the command is immediately followed by an integer (c) which defines the capacity of the list. For a list with 0 specified capacity, create a "default" (MAX_SIZE) capacitly list. DDestroy the current List object +xThe '+' command performs an insert operation on the current list. The value to insert (x) immediately follows the '+' and is of type consistent with the type of the list (an integer list for your submission). In general, this should be read into an Item typed variable. -Perform a remove operation at the current cursor position . @Display to cout the data item at the current cursor position (or the character sequence "EOL" if at the end of the list). *Display to cout the entire list, including an indication of the current cursor position. Output should conform to the specification above. <Go to the beginning of the list. NAdvance the cursor to the next item in the list. EReport whether the list is empty, printing TRUE or FALSE. FReport whether the list is full, printing TRUE or FALSE. $Report whether the current position is EOL, printing TRUE or FALSE. =Demonstrate assignment overload, assigning the alternate list to the current list. &Demonstrate copy constructor, creating a new current list from the alternate list. ~Switch the current list with the alternate list. XDelete all elements in the list. HPrint a help message with the supported commands and a brief description of their actions. QQuit the test program. You will also detail a test plan that gives good coverage demonstrating that your implementation works and _then_ implement the test plan with a driver program named testList.cpp.
You should be able to use much of the infrastructure that you have already built in Homework 4, particularly the driver program. To keep focused, the functionality of the List will be submitted in stages. In the first stage, you will implment the Node class and the basic List class operations of constructor, insert(), remove(), reset(), advance(), getCurrent(), atEOL(), isEmpty(), and isFull() as detailed above. This basic functionality will be due on Friday, February 29 at class time. In the second stage, you will add the copy constructor for the List, the List destructor that deallocates all resources of the List and its constituent Node objects, as well as an overload of the assignment operator and an overload of stream insertion to clean up the way we do output. These enhancements are due by midnight on Monday, March 3.