A queue has three main operations:
These operations are defined in /home/plank/cs140/include/queue.h. To use them, you must link your code with /home/cs140/objs/queue.o. /home/plank/cs140/objs/queue.o. Since queue.h makes use of Jval's, you'll have to link with libfdr.a as well.
Queuesimp.c shows a very simple example of using a queue. First, we enqueue three integers on the queue -- 1, 2 and 3. Then we call queue_dequeue() twice, and print out the values. Finally, we push 4 onto the queue, and call queue_dequeue() twice more, printing out the values.
Here's the code:
#include <stdio.h>
#include "jval.h"
#include "queue.h"
main()
{
Queue q;
int i;
q = new_queue();
queue_enqueue(q, new_jval_i(1));
queue_enqueue(q, new_jval_i(2));
queue_enqueue(q, new_jval_i(3));
i = jval_i(queue_dequeue(q));
printf("First dequeue: %d\n", i);
i = jval_i(queue_dequeue(q));
printf("Second dequeue: %d\n", i);
queue_enqueue(q, new_jval_i(4));
i = jval_i(queue_dequeue(q));
printf("Third dequeue: %d\n", i);
i = jval_i(queue_dequeue(q));
printf("Fourth dequeue: %d\n", i);
}
|
And here's its output:
Queues are conceptually simpler than stacks -- a queue is simply like any line you wait in -- first come, first serve. To give it the same visual picture as the stack, again, we maintain a list of items, but this time we put items in one side, and take them off the other.UNIX> queuesimp First dequeue: 1 Second dequeue: 2 Third dequeue: 3 Fourth dequeue: 4 UNIX>
Thus, after the first three calls to queue_enqueue(), we have a list (1,2,3). The first call to queue_dequeue() removes the 1, and leaves (2,3). The second call to queue_dequeue() removes the 2, and leaves (3). The last call to queue_enqueue() turns the list into (3,4), and the subsequent queue_dequeue() calls return 3 and 4 respectively.
Here's the code (in queuetail.c):
#include <stdio.h>
#include <string.h>
#include "queue.h"
#include "fields.h"
main(int argc, char **argv)
{
Queue q;
int n, i;
IS is;
Jval v;
if (argc != 2) {
fprintf(stderr, "usage: queuetail n\n");
exit(1);
}
n = atoi(argv[1]);
if (n < 0) exit(1);
q = new_queue();
is = new_inputstruct(NULL);
while (get_line(is) >= 0) {
queue_enqueue(q, new_jval_s(strdup(is->text1)));
if (queue_size(q) > n) {
v = queue_dequeue(q);
free(v.s);
}
}
while (!queue_empty(q)) {
printf("%s", jval_s(queue_dequeue(q)));
}
}
|
Like stacks, you may implement queues by having a linked data structure. Unlike a stack, each node on a queue points to the next node enqueued, so the pointers go from the front of the queue to the rear. Moreover, you must have two pointers in your header structure -- a pointer to the front and a pointer to the rear. The code is in queue.c.
First, here are the struct declarations:
typedef struct queue_node {
struct queue_node *link;
Jval val;
} Queue_Node;
typedef struct {
int size;
Queue_Node *front;
Queue_Node *rear;
} True_Queue;
And here are three examples of queues:
As with stacks, we'll only go over the subtle routines -- queue_enqueue() and queue_dequeue(). First, queue_enqueue() allocates a new node, and puts it behind the rear node:
void queue_enqueue(Queue q, Jval jv)
{
True_Queue *tq;
Queue_Node *qn;
tq = (True_Queue *) q;
qn = (Queue_Node *) malloc(sizeof(Queue_Node));
if (qn == NULL) { perror("malloc"); exit(1); }
qn->link = NULL;
qn->val = jv;
if (tq->size == 0) {
tq->front = qn;
tq->rear = qn;
} else {
tq->rear->link = qn;
tq->rear = qn;
}
tq->size++;
return;
}
To dequeue a node, you remove the front node of the queue, and set front to the next node on the queue:
Jval queue_dequeue(Queue q)
{
True_Queue *tq;
Jval v;
Queue_Node *qn;
tq = (True_Queue *) q;
if (tq->size == 0) {
fprintf(stderr, "Error: queue_dequeue called on an empty queue\n");
exit(1);
}
v = tq->front->val;
qn = tq->front;
tq->front = qn->link;
free(qn);
tq->size--;
return v;
}
Redrawn, it looks as follows: