CSCI 2720 Data Structures Spring 2004 Programming Project 1 - due Thursday January 29, by 12:00 PM. See the link from the course home page to General Project Instructions for instructions which apply to all of the programming projects. The project has three parts. The first is to implement a list class using templates and nodes singly linked by pointers. The second part is to implement a modified version of the list class in which data items are stored according to a linear order. These will be compared by timing tests in the third part of the project. Part I. UList Class Specify and implement a list class UList based on a singly linked list structure using templates and dynamic memory allocation. Dtype is a generic type for the list data. You don't know in advance whether or not == is defined for Dtype, so design your constructor so that == is the default but a pointer to a user-supplied fuction can be passed in as a parameter to define equality (returning an integer value which is 0 if and only if the two items of Dtype are not to be considered equal). You should read Chapter 13 of Stroustrup to see how templates are handled. Section 13.2.5 contains an example of part of a doubly linked list class which you can modify and extend. A difference at the lowest level will be that you will only need one pointer field for each node. You should hide your node class declaration inside the the way Stroustrup does with Link. Supply public functions Find( ), Insert( ), and Remove( ); these must each take a const Dtype& input and return an int. Each should return 1 if the operation was successful, and 0 if not (i. e., return 0 if you try to insert a key already present in the tree, or find or delete a key which is not present). Remove( ) must be real rather than lazy. So when you remove a key which is present in the list, the node containing that key should be deallocated by delete. The rest of the keys should maintain their relative order in the list, i.e., don't rearrange the list during a removal. Along the same lines, inserting a key not present in the list should be accomplished by allocating a node by new to hold the new key, and that node should be added at the tail of the list. Also supply public function Display( ); this should output the keys to cout in list order from head to tail, 10 keys per line. The only other public functions you need to supply are the constructor and the destructor. The constructor should create an empty list and the destructor should deallocate all memory which was dynamically allocated to the list. Your specification should be in a file called ulist.h and your implementation code should be in ulist.cc. Before going on to part II you should test your UList< > class using Find( ), Insert( ), Remove( ), and Display( ). Make sure it works for int data and the default comparison, and on C style strings with the strcmp function for comparison. For the latter, you'll need to wrap strcmp in a function of your own and pass the address of your function as the parameter for the constructor. Part II. OList class Modify Part I to define and implement a list class which maintains its data according to a linear ordering of the keys. The ordering is to be defined by a private member function which, given inputs (x,y) of type Dtype returns -1 if x should come before y in the order, 1 if y should come before x in the order, and 0 if x and y are equal. As in Part I a pointer to a user-supplied comparison function should be an optional parameter to the constructor; the default should be to have x appear before y in the ordered list if x < y, which will only work if operator< is defined for Dtype. The public functions should be the same as in Part I except for maintaining and using the list ordering property. Your specification should be in a file called olist.h and your implementation code should be in olist.cc; the testing of OList should follow the specifications set out for UList in Part I. Part III. Timing Comparisons The aim is to create lists of type UList and OList of length n for several (say 5) evenly spaced values of n and measure the average time for successful and unsuccessful Find( )s on those lists. A convenient way to accomplish this which effectively randomizes order in the UList list is based on a relatively prime pair of integers range and factor. For i = 1...(range - 1) the numbers j = (i*factor) % range will will run through the same set of values 1...(range - 1) in a different order. Just choose factor to be somewhere near the middle of the range and share no divisor with range other than 1 and factor. For example, 1024 for range and 611 for factor. To use this scheme, start with empty lists and enter 2*j into each, as the j values are generated. Then performing Find(x) for x = 2, 4, ..., 2*(range - 1) will provide range - 1 successful Find( )s whereas doing it for x = 1, 3, ..., 2*range -1 will provide range unsuccessful Find( )s. The code for your timing runs should be in timing.cc, and the times you obtain should be tabulated in timing.results along with a discussion of their significance. Answer the questions "How does UList compare to OList?" and "What effect does the length of the lists have on the average Find( ) times, both successful and unsuccessful?".