CSC 161 | Grinnell College | Spring, 2009 |
Imperative Problem Solving and Data Structures | ||
This laboratory applies the concept of circular lists to implement a queue.
While the linear, singly-linked list structure just described works fine, the implementation is sometimes considered inelegant. Specifically, a queue variable utilizes an entire structure, and this structure contains two fields. Also, coding requires several special cases when the queue is empty or has just one element.
As an alternative, the list may be made circular, with the last item pointing to the first. With this modification, the head pointer is no longer needed. Such a structure is shown in the following diagram:
Such a perspective utilizes exactly the same node structure defined previously. However, with this revised picture, a queue variable reduces to just the rear pointer:
/* Maximum length of names */ #define strMax 20 typedef struct node { char data [strMax]; struct node * next; } queueNode; typedef queueNode * stringQueue; stringQueue queue;
Operations on this queue are quite similar to those for the singly-linked list version:
void initializeQueue (stringQueue * queue) sets the queue pointer to NULL.
int empty (stringQueue queue) tests the queue pointer against NULL.
int full (stringQueue queue) returns false, just as was done in the regular linked-list implementation.
int enqueue (stringQueue * queue, char* item) adds an element to the rear of the list: creating a new node, linking to it as the next item after the current last item, and updating the queue variable. If the new item is the only one on the queue, then its next pointer specifies the node itself. The function returns the length of the string added.
char * dequeue (stringQueue * queue) retrieves the data in the head node (the node after the tail one), moves head to the second list item, deallocates the space of the old first node, and returns the data in that former head of the queue. In addition, if this item had been the last one on the queue, then queue must be updated to NULL.
Copy your program from the lab on Queues with Lists. (You will need that previous program later, so be sure you work on a new copy!)
Change the queue implementation from a linear linked list to a circular list, and adjust the queue functions as needed, but retain all testing in the main procedure.
Test this new program using the same tests used for the linear linked-list implementation. As part of this testing, use the same input redirection that you used for testing the linked-list version. That is, if the name of the old program was queues-list.c and the name of the new program is queues-circular.c, follow these steps:
gcc -o queues-list queues-list.c queues-list < queue-test.dat gcc -o queues-circular queues-circular.c queues-circular < queue-test.dat
You should be able to visually scan the two test results to check that the results are identical.
We now try to automate the process for comparing tests, based on the queue-test.dat test cases. The idea is to write the test results of both programs to separate files and then use the Linux command diff to identify any differences.
Test in a terminal window, adjusting the above test runs by redirecting output to a file and then applying diff. The relevant commands are:
gcc -o queues-list queues-list.c queues-list < queue-test.dat > queue-list.out gcc -o queues-circular queues-circular.c queues-circular < queue-test.dat > queue-circular.out diff queue-list.out queue-circular.out
If the two programs produce identical output, diff should print nothing, as only different lines of output are reported.
To check the use of diff, add a printf statement to the main function of the circular-list implementation, and repeat the above testing with diff. Just the newly-generated line should appear.
To automate testing further, place the above terminal commands in a file queue-automated-test. For clarity, you might add a few echo statements — perhaps as follows:
echo "compiling and running queues-list.c" gcc -o queues-list queues-list.c queues-list < queue-test.dat > queue-list.out echo "compiling and running queues-circular.c" gcc -o queues-circular queues-circular.c queues-circular < queue-test.dat > queue-circular.out echo "comparing program results" diff queue-list.out queue-circular.out echo "end of test"
Now set the protection code of that file to include execute capability, and execute the program in a terminal window:
chmod 700 queue-automated-test queue-automated-test
Run this test script and explain briefly what is printed.
Note that this approach to testing can be extremely useful when one wants to add new features to a program, while continuing to process existing commands in the same way. A testing script can compare the results of the old and revised programs on the old commands. With redirection and diff, only the changed results will be reported.
This document is available on the World Wide Web as
http://www.walker.cs.grinnell.edu/courses/161.sp09/lab-queues-circular.shtml
created 27 September 2001 last revised 25 January 2009 |
![]() ![]() |
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |