CS 1321 (Principles of Algorithm Design II):
Homework #6

Assigned:
March 23, 2000
Due:
March 31, 2000, at the beginning of class. Note: Not accepted later than 5pm March 31.
Credit:
20 points.

Contents

Problem statement

You and possibly a CS1321 classmate are to implement a doubly-linked list class using dynamic memory and links. Although similar to the dll doubly-linked list class presented in lecture, your class's implementation is to be linear and may not have a sentinel. This implementation is the one programmers usually use if they do not know about the circular implementation with a sentinel.

Introduction

A doubly-linked list is a data structure with objects arranged in linear order and permitting easy access to both previous and next items. For example, the STL list class implements a doubly-linked list. (Interestingly, STL creator Alex Stepanov apparently also decided to use a circular implementation with a sentinel.)


 
Table 1: dll Member Functions and Types
function prototype example use explanation
dll(void) dll lst; create a list with no items
item_type dll::item_type i = 'a'; type specifying a list entry
link dll::link * lnk = lst.erase(lnkPtr); type holding a list element

link * insert(link * pos, const item_type & item)

lnkPtr = lst.insert(lnkPtr, 'b'); adds the specified item before the specified position
link * erase(link * linkPtr) dll::link * lnk = lst.erase(lnkPtr); remove the specified item from the list
link * begin(void) const dll::link * lnkB = lst.begin(); return a pointer to the first item in the list
link * end(void) const dll::link * lnkE = lst.end(); return a pointer past the last item in the list
link * pred(const link * lnk) const dll::link * lnk = lst.pred(lnkE); return list item just before the given item
link * succ(const link * lnk) const dll::link * lnk = lst.succ(lnkB); return list item just after the given item
 

Your implementation should support the same operations as the dll doubly-linked list class presented in lecture plus a few more. That is, it should support all the operations listed in Table 1. When inserting an item into a list, the item's new link should be to the left of the user-specified position. If this position is a ground (null) pointer, one should insert at the list's right end. To insert at the left end, the user should specify the leftmost link. To erase a link, the user need only specify the link to erase. Unlike the implementation presented in class, insert should return a pointer to the inserted link. The erase() function returns the link to the right of the erased link or a ground if the rightmost link was removed. Both functions may assume that their parameter values are valid.

The last four operations permit the user to ``move'' through the items in the list. begin and end return pointers to a list's first link and one past its last link, respectively. Given a pointer into the list, pred and succ return pointers to the preceding link (link to the left), and subsequent link (link to the right), respectively. The predecessor of the leftmost link is a ground pointer as is the successor of the rightmost link. When given a ground pointer, pred assumes the ground is the right end of the list. When given a ground pointer, succ assumes the ground is the beginning of the list.

Please ensure that, if a list is copied, changing the original list's contents does not change the copy and vice versa. (Hint: Write an assignment operator and a copy constructor if necessary.) Be sure not to leak dynamic memory. (Hint: Write a destructor function if necessary.)

For now, assume the list contains chars, but, anticipating future modification to hold any particular type, we require using item_type.

Naive linear implementation

In class, we presented algorithms and code ( dll.h) for a circular implementation with a sentinel link. In this assignment, we will use a linear implementation with no sentinel. For example, here are

The number of links equals the number of items in the list, and ground (null) pointers appear at the left and right ends of the list. In C++, ground pointers are represented as 0, i.e., zero.

(Note: I prefer to reference null pointers using the macro NULL, which can be found in library include file cstddef. To do this, include the line

    #include <cstddef>
You can then write code such as the following
    int * p;
    p = NULL;
    if (p == NULL) cout << "p null\n";
Or you can omit the #include and replace NULL with 0.)

The linear implementation will be very similar to the circular, sentinel implementation except that the routines cannot always assume that there is a link to the left and right when inserting and erasing. For example, consider inserting a link at the right end of a list with one link. We are given a ground pointer. Assume we have already created a link named n to hold the new item.

\includegraphics*{hw6-eps/hw6.100}
We need to know the link to the left. Thus, let us assume the dll object maintains a link * r always pointing to the list's rightmost link. The first step is to set n's pointers to the correct locations.
\includegraphics*{hw6-eps/hw6.110}
The next step is to change the surrounding links' pointers. Since there is no link to the right of n, we cannot change its link. Finally, we update the object's pointer to the rightmost link.
\includegraphics*{hw6-eps/hw6.120}
This implementation differs from the circular, sentinel implementation because we needed a pointer to the list's rightmost link and we did not change the pointer of the link on the right.

Correctness and testing

Your implementation should support insertion and erasure anywhere in a list, e.g., at the beginning, middle, or end, for a list of any length, e.g., empty, one link, or multiple links. Before writing code, we strongly recommend that you draw pictures of all possible cases and annotate with the associated code. Otherwise, the probability of obtaining a correct implementation is minimal.

Our recommended strategy for implementing and testing your code is to interleave writing member functions, compiling, and testing. That is, choose an order for implementing member functions that minimizes the amount of code written between compilations and testing.

  1. Write a very simple test program that declares a doubly-linked list class but nothing else.
  2. Write the dll skeleton, i.e., class dll, braces, a semicolon, and nothing else.
  3. Compile, fixing all errors.
  4. Add definitions for item_type and link.
  5. Compile, fixing all errors.
  6. Add a definition for insert. This may require adding helper functions and private variables.
  7. Revise the test program to test inserting at the beginning of the list, at the end of the list, and in the middle of the list.
  8. Compile, fixing all errors. Check for correctness. Check for memory leaks.
  9. Continue the process, implementing erase last.
To speed programming, your partner could revise the test code to use a particular member function while you are programming it. After compiling and testing, you can swap roles for the next member function. It may also be useful to overload the output operator to print a list's contents.

After you have finished the implementation, try using the buffer code. It uses a dll class but assumes that member functions link * begin(void) const and link * end(void) const are also * defined (which they will be in your revised class). editor.cc uses the buffer class to implement the brain-dead string editor presented in lecture.

Your implementation should correctly use dynamic memory. For example, dynamic memory should be deleted when no longer used, and deleted memory should not be used. If you wish to use the mtrace command to help find memory-use errors, here are the instructions for doing so:

  1. In your test program, add
        #include <mcheck.h>
    

    and, at the beginning of main(), add

        mtrace();
    

    Let's assume the executable is named a.out.

  2. Before running the executable, type
        declare -x MALLOC_TRACE=foo.txt
    
    in a shell. (You can replace foo.txt with any desired file name.)

  3. Run the executable. Memory allocation and deallocation information is stored in the file called foo.txt.

  4. To print memory leak information, type
        mtrace a.out $MALLOC_TRACE
    
    in the same shell.
For more information, see the info pages. For example, using emacs, type Control-h i, m libc, m memory allocation, and m allocation debugging.

Caveats:

What files do I need?

We suggest starting with the circular, sentinel implementation dll.h and converting it to a linear implementation.

The lecture version of the buffer code has been modified to use begin, end, pred, and succ. Revised code can be found in this link to buffer.h.

Rules

Programming rules

As for previous homeworks, working with other people during your planning phase is encouraged. For this homework, you are permitted to write the code, i.e., program, with one other person in CS1321. To learn the material, both of you should be actively involved in the programming. Failure to do so will almost certainly hurt your comprehension of the material in the rest of the course.

Submission rules

Each one- or two-person team of programmers should submit only its completed implementation in a file named dll.h. You need not send any other files. Please send only text documents, do not send Microsoft Word documents, PDF documents, HTML documents, etc. Please include both your names and email addresses at the top of your program.

We will test your code using our own main() function. Please be sure it compiles without warning when using g++ -Wall -pedantic.

See the guidelines for programming assignments for instructions on how to e-mail the programs. For this assignment use a subject line of ``cs1321 homework 6''.

If a team of two are in different sections, submit exactly once to one of the two permissible email addresses.


Footnotes

... list,2
This looks like Darth Vader's ship in episode 4 of Star Wars. Just as Darth was the rebels' bane, the empty list will be your bane.

Copyright notice

©2000 Jeffrey D. Oldham . All rights reserved. This document may not be redistributed in any form without the express permission of the author.