CSC 161 Grinnell College Fall, 2011 Imperative Problem Solving and Data Structures

Abstract

This reading provides you with some experience using doubly-linked lists and also highlights a process of incremental program development.

Acknowledgement

Most of this reading is an edited version of Henry M. Walker, Computer Science 2: Principles of Software Engineering, Data Types, and Algorithms, Little, Brown, and Company, 1989, Section 5.6, with programming examples translated from Pascal to C. This material is used with permission from the copyright holder.

Introduction

Up to this point, all lists have been structured so that we can start at the front and move efficiently item by item to the end. However, in many applications, we need more flexibility than this. Consider, for example, the following problem.

Problem: Recording and Retrieving Golf Scores

In recording scores for a golf tournament, we enter the name and score of the player as the player finishes. This information is to be retrieved in each of the following ways:

• Scores and names can be printed in order by ascending or by descending scores.
• Given the name of a player, other players with the same score can be printed.

Discussion

This problem requires the frequent updating of names and scores and the ordering of the information. Such requirements naturally suggest a linked list structure. However, here a simple linked list is difficult to use efficiently because all the pointers move in one logical direction from the start to the end. This structure makes it hard to move backward from a given name.

To resolve this difficulty, we consider a new type of structure, called a doubly linked list, in which each item contains a pointer to the previous item as well as to the next one. The following figure shows a doubly linked list for this problem.

This figure illustrates several important features of a doubly linked list. First, each item on the list contains two pointers as well as some data. For example, this doubly linked list might use the following declarations:

``` /* Maximum length of names */ #define strMax 20 struct dnode { int score;   char player [strMax];   struct dnode * prev;   struct dnode * next; }; struct dnode * first;  /* pointer to the first list item */ struct dnode * last;   /* pointer to the last list item */ ```

With these declarations, the data fields store appropriate scores and names, the prev field points to the previous item on the list, and the next field specifies the next item on the list. The first and last pointers give the ends of the list, and we use a NULL to specify an end of the list.

With this structure, we can perform the same general operations discussed earlier for simple lists, namely:

• printing the data on the list,
• inserting or deleting an item, and
• finding and perhaps modifying an item.

Here, however, the pointers going backward as well as forward simplify some of these operations and expand processing capabilities.

Printing

In a doubly-linked list, printing the list in reverse order parallels printing in the normal order. To print from first item to last, we start with the item specified by the first pointer and then proceed item by item using the next field of each item. To print in reverse order, we start with the item specified by the last pointer and then proceed item by item using the prev fields of each item.

Insertion and Deletion

The insertion and deletion operations follow the same general steps discussed for simple lists, although processing must handle backward pointers as well as forward ones. The main steps for insertion into a doubly linked list follow:

1. Determine where item will be inserted.
2. Create a new box or record.
3. Place data in new record.
4. Make prev and next pointers of new record specify the appropriate list elements.
5. Update the next pointer in the previous record to specify the newly created record.
6. Update the prev pointer in the next record to specify the newly created record.

With this outline, the programming details for doubly linked lists parallel the code for singly linked lists, although now we must modify the prev pointers as well as the next ones. The results of insertion are shown in the following figure:

The deletion operation for doubly linked lists requires similar modifications.

Final Note Regarding Searching

Although we must maintain two sets of links for each node, the task of searching for a node sometimes is simplified for a doubly-linked list over a singly-linked list. In particular, when locating a node for insertion or deletion within a singly-linked list, the search typically requires that we maintain a previous pointer as well as a pointer to the current node. When performing the corresponding task for insertion or deletion in a doubly-linked list, of course, we need not include the previous pointer in the search process. Once we find a relevant node, we can use the prev or next field of the identified node to locate the adjacent list elements, so our search does not have to maintain that information directly.