| CSC 201 | Grinnell College | Spring, 2005 |
| Data Representation, Memory Management, and Formal Methods | ||
This laboratory applies the concept of circular lists to implement a queue.
From your past work, you know that a queue is a data structure that provides access to its elements on ``first-in, first-out'' basis, rather than the ``last-in, first-out'' constraint that a stack imposes. (For example, it might be prudent to treat that pile of unpaid bills a little differently, adding new elements at the bottom of the pile rather than the top. Paying off the most recent bill first, as in a stack, can make one's other, older creditors a little testy.) /p>
Like a line of people waiting for some service, a queue acquires new elements at one end (the rear of the queue) and releases old elements at the other (the front). While queues sometimes are given a large number of properties, the basic queue operations are as follows:
create
Create a new, empty queue object.
empty
Determine whether the queue is empty; return true if it is and
false if it is not.
enqueue
Add a new element at the rear of a queue.
dequeue
Remove an element from the front of the queue and return it. (This
operation cannot be performed if the queue is empty.)
While queues may be implemented in many ways, one of the simplest
conceptually uses a singly-linked list with pointers front and
rear giving access to the two ends:
With this perspective, an element on a queue is stored in a data field within a list node:
struct node {
char data [maxLength];
struct node * next;
}
The queue itself involves two pointer variables:
struct queue {
struct node * front;
struct node * rear;
}
With such a structure, each queue operation is reasonably straightforward:
create declares a variable of type struct queue and
sets both front and rear fields to NULL.
empty tests front (or rear)against
NULL.
enqueue 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 rear variable. In addition, if the queue had
been empty, then front also must be updated to the new node.
dequeue retrieves the data in the front node, moves
front 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
rear must be updated to NULL.
While enqueue and dequeue require a little care to handle empty lists, the code follows the outline fairly closely.
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.
As an alternative, the list may be made circular, with the
last item pointing to the first. With this modification, the
front 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:
struct node * queue; /* pointer specifing the rear of the queue structure */
Operations on this queue are quite similar to those for the singly-linked list version:
create declares a variable of type struct node * and
sets this pointer to NULL.
empty tests the queue pointer against NULL.
enqueue 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.
dequeue retrieves the data in the front node (the node after
the rear one), moves front 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.
Program ~walker/c/lists/queue.c contains a shell for maintain
the standard queue operations. In addition, a print operation is added,
so one can check all elements on a queue. Of these operations, however,
only the empty and print functions are implemented.
enqueue operation. Note that this function
could be tested with the empty and print functions.
dequeue operation.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~walker/courses/201.sp05/lab-queues.shtml
|
created September 27, 2001 last revised February 11, 2005 |
|
| For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |