/* |
Queue of int, implemented by a circular buffer in array |
*/ |
|
#ifndef _queue_h |
#define _queue_h |
|
/* queue Abstract Data Type |
|
list points to an array that contains the elements in queue |
|
length stores the number of items currently in the queue |
|
max_length stores the total space allocated for the queue list |
|
head_index stores the index of the current head of the list, |
which changes as items are removed from the list |
|
Invariant: The head of the queue starts at position list[head_index], |
and goes up to position list[head_index + length -1]. In the |
event that head_index + length -1 > max_length-1, the list wraps |
around to position 0. |
|
Thus the current items in the queue are at positions |
head_index, head_index+1, ..., max_length-1, |
0, ..., (length-1) % max_length |
*/ |
typedef struct { |
int *list; |
int length; |
int max_length; |
int head_index; |
} queue; |
|
void initializeQueue(queue *q, int size_hint); |
void addElement(queue *q, int val); |
int removeElement(queue *q); |
|
#endif |
queue
contains all the information we will need for maintaing the FIFO data structure. In the below code, we see how to add and remove elements from the queue, which are the only two required operations on the abstract data type. We also need a function to increase the size of the list in the queue, if we add more elements than we originally allocated in the queue. /* Implementation of Queue ADT using circular list in an array */ |
#include "queue.h" |
#include <stdlib.h> |
#include <assert.h> |
|
/* |
Take a declared empty queue structure and initialize it. |
|
Warning: Do not call on an existing queue. Will get a |
memory leak. |
*/ |
void initializeQueue(queue *q, int size_hint) { |
assert(q != 0); |
|
q->max_length = size_hint; |
q->length = 0; |
q->head_index = 0; |
q->list = malloc(q->max_length*sizeof(int)); |
assert(q->list != 0); |
} |
|
/* internal method to increase size of queue */ |
void increaseSize(queue *q, int increase) { |
int new_max_length = q->max_length + increase; |
|
int * bigger_list = malloc(new_max_length * sizeof(int)); |
assert(bigger_list != 0); |
|
for (int i = 0; i < q->max_length; i++) { |
bigger_list[i] = q->list[i]; |
} |
|
free(q->list); |
q->list = bigger_list; |
q->max_length = new_max_length; |
} |
|
|
/* |
Add the element val to the tail of the queue q |
*/ |
void addElement(queue *q, int val) { |
if (q->length == q->max_length) { |
/* double the current length of the queue */ |
increaseSize(q, q->max_length); |
} |
|
int new_index = (q->head_index + q->length) % q->max_length; |
q->length++; |
|
q->list[new_index] = val; |
} |
|
/* |
Remove and return the element at the head of queue q |
|
Pre-condition: q->length > 0 |
*/ |
int removeElement(queue *q) { |
assert(q->length > 0); |
|
// Obtain the current head of the list |
int current_head_item = q->list[q->head_index]; |
|
q->length--; |
|
/* |
Since the head has been removed, shift the head index to |
the next position. If we are at the end of the list, |
then we wrap around to the beginning. |
*/ |
if (q->length == 0) { |
/* empty queues start at position 0 */ |
q->head_index = 0; |
} else { |
q->head_index = (q->head_index + 1) % q->max_length; |
} |
|
return current_head_item; |
} |
#include <stdio.h> |
#include <stdlib.h> |
#include "queue.h" |
|
/* |
print the contents of a queue from head to tail going left |
to right. |
|
NOTE: This code is fragile as it inspects the internal |
representation of the queue. |
|
*/ |
void printf_queue(queue *q) { |
int pos = q->head_index; |
for (int i=0; i < q->length; i++) { |
printf("%d ", q->list[pos]); |
pos = (pos + 1) % q->max_length; |
} |
} |
|
int main(int argc, char *argv[]) { |
/* test harness */ |
int tests_passed = 0; |
int tests_failed = 0; |
|
/* create a queue instance and initialize it */ |
queue q_instance; |
|
/* create a handle to the queue so that we can manipulate it |
without having to put the & in front all the time */ |
|
queue *qp = &q_instance; |
|
printf("Initializing queue\n"); |
initializeQueue(qp, 20); |
|
/* how do we see if queue was propery initialized? */ |
|
/* add and remove one element */ |
int e1 = 42; |
|
addElement(qp, e1); |
printf("Test 1: "); |
printf_queue(qp); |
printf("\n"); |
|
/* length should have gone up by one */ |
|
if ( qp->length != 1 ) { |
printf("Test 1 failed, length %d should be 1\n", |
qp->length); |
tests_failed++; |
} |
else { |
printf("Test 1 passed.\n"); |
tests_passed++; |
} |
|
|
printf("Test 2: "); |
int e2 = removeElement(qp); |
printf_queue(qp); |
printf("\n"); |
|
if ( qp->length != 0 ) { |
printf("Test 2.1 failed, length %d should be 0\n", |
qp->length); |
tests_failed++; |
} |
else { |
printf("Test 2.1 passed.\n"); |
tests_passed++; |
} |
|
if ( e1 != e2 ) { |
printf("Test 2.2 failed, e2 %d should equal e1 %d\n", |
e2, e1); |
tests_failed++; |
} |
else { |
printf("Test 2.2 passed.\n"); |
tests_passed++; |
} |
|
printf("Test 3: "); |
for (int i=1; i <= 10; i++) { |
addElement(qp, i); |
} |
printf_queue(qp); |
printf("\n"); |
for (int i=1; i<= 10; i++) { |
e1 = removeElement(qp); |
if ( qp->length != 10-i ) { |
printf("Test 3.1 failed, length %d should be %d\n", |
qp->length, 10-i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
if ( e1 != i ) { |
printf("Test 3.2 failed, element %d should be %d\n", |
e1, i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
} |
|
|
/* a fatal test */ |
if ( 0 ) { |
printf("Test 4: remove on empty queue\n"); |
e2 = removeElement(qp); |
tests_failed++; |
} |
|
printf("Tests Passed: %d\n", tests_passed); |
printf("Tests Failed: %d\n", tests_failed); |
} |
CFLAGS = -std=gnu99 -Wall -Werror |
|
all : testq |
|
# the queue implementation depends on the header also |
queue.o : queue.c queue.h |
|
# testq also depends on the queue library |
testq : testq.c queue.o |
$(CC) $(CFLAGS) testq.c queue.o -o testq |
|
clean : |
rm -f queue.o testq |
getElement
function that gets the element at a given index in the queue, where 0 is the head of the queue, and length(q)-1 is the tail. k
different queues in a grocery store (for the deli, for the bakery, for the cashier, etc.). The length of the queues will be changing quite dynamically, with the largest queue at the deli at one point and then maybe another time at the cash. Instead of having to equally allocate some maximum amount of memory for each queue, memory can dynamically be allocated by using a linked list of only the current number of elements in each queue. In this way, the memory is allocated to the queue that needs it at any given time. /* |
Queue of int, implemented by a linked list |
*/ |
#ifndef _queue_h |
#define _queue_h |
|
/* |
A linked list contains nodes which hold the information for |
that node (val) and the pointer to the next node in the |
list (next). |
|
When next is 0 (null pointer) there is no following cell. |
*/ |
typedef struct node { |
int val; |
struct node *next; |
} node; |
|
/* |
The queue is implemented by keeping a pointer to the head |
node of a linked list. To make adding nodes to the queue |
efficient, we also have a pointer to the tail node of the |
list. |
|
A queue is manipulated by passing around a pointer to its |
queue struct. |
*/ |
typedef struct { |
node *head; |
node *tail; |
int length; |
} queue; |
|
void initializeQueue(queue *q, int size_hint); |
int length(queue *q); |
void addElement(queue *q, int val); |
int removeElement(queue *q); |
int getElement(queue *q, int index); |
|
#endif |
/* |
Implementation of queue Abstract Data Type (ADT) |
*/ |
|
#include <stdlib.h> |
#include <stdio.h> |
#include <assert.h> |
#include "queue.h" |
|
/* |
Take a declared empty queue structure and initialize it. |
|
Warning: Do not call on an existing queue. Will get a |
memory leak. |
*/ |
|
void initializeQueue(queue *q, int size_hint) { |
q->head = 0; |
q->tail = 0; |
q->length = 0; |
} |
|
/* |
Get the current length of the queue |
*/ |
int length(queue *q) { |
return q->length; |
} |
|
/* |
Add the element val to the tail of the queue q |
*/ |
|
void addElement(queue *q, int val) { |
node * n = (node *) malloc(sizeof(node)); |
|
// make sure malloc worked |
assert(n != 0); |
|
// initialze the new node to contain the value and have no next |
n->val = val; |
n->next = 0; |
|
// if there is stuff in the queue, add new node to end |
// if an empty queue, need to set up head. |
if (q->length != 0) { |
q->tail->next = n; |
} else { |
q->head = n; |
} |
|
// new node is at the tail |
q->tail = n; |
q->length++; |
} |
|
/* |
Get the element of the queue at position index, |
where the head of the queue is position 0, and the |
tail is at position length(q)-1 |
*/ |
int getElement(queue *q, int index) { |
assert(0 <= index && index < q->length); |
return 0; |
} |
|
/* |
Remove and return the element at the head of queue q |
|
Pre-condition: q->length > 0 |
*/ |
|
int removeElement(queue *q) { |
assert(q->length > 0); |
|
// get the value at the head |
int rval = q->head->val; |
|
// new head is the next one |
node *new_head = q->head->next; |
|
/* Now we can free the old head so that we don't leave unused |
memory allocated |
*/ |
free(q->head); |
|
q->length--; |
|
// Now we can set the head to new_head |
q->head = new_head; |
|
// if queue is empty, we need to set tail also. |
if (q->length == 0) { |
q->tail = 0; |
} |
|
return rval; |
} |
#include <stdio.h> |
#include <stdlib.h> |
#include "queue.h" |
|
/* |
print the contents of a queue from head to tail going left |
to right. |
|
NOTE: This code is fragile as it inspects the internal |
representation of the queue. |
|
What we really want is: |
for (int i=0; i < length(q); i++) { |
printf("%d ", getElement(q, i)); |
} |
*/ |
void printf_queue(queue *q) { |
node * cur = q->head; |
while( cur != 0 ) { |
printf("%d ", cur->val); |
cur = cur->next; |
} |
} |
|
|
int main(int argc, char *argv[]) { |
/* test harness */ |
int tests_passed = 0; |
int tests_failed = 0; |
|
/* create a queue instance and initialize it */ |
queue q_instance; |
|
/* create a handle to the queue so that we can manipulate it |
without having to put the & in front all the time */ |
|
queue *qp = &q_instance; |
|
printf("Initializing queue\n"); |
initializeQueue(qp, 20); |
|
/* how do we see if queue was propery initialized? */ |
|
/* add and remove one element */ |
int e1 = 42; |
|
addElement(qp, e1); |
printf("Test 1: "); |
printf_queue(qp); |
printf("\n"); |
|
/* length should have gone up by one */ |
|
if ( qp->length != 1 ) { |
printf("Test 1 failed, length %d should be 1\n", |
qp->length); |
tests_failed++; |
} |
else { |
printf("Test 1 passed.\n"); |
tests_passed++; |
} |
|
|
printf("Test 2: "); |
int e2 = removeElement(qp); |
printf_queue(qp); |
printf("\n"); |
|
if ( qp->length != 0 ) { |
printf("Test 2.1 failed, length %d should be 0\n", |
qp->length); |
tests_failed++; |
} |
else { |
printf("Test 2.1 passed.\n"); |
tests_passed++; |
} |
|
if ( e1 != e2 ) { |
printf("Test 2.2 failed, e2 %d should equal e1 %d\n", |
e2, e1); |
tests_failed++; |
} |
else { |
printf("Test 2.2 passed.\n"); |
tests_passed++; |
} |
|
printf("Test 3: "); |
for (int i=1; i <= 10; i++) { |
addElement(qp, i); |
} |
printf_queue(qp); |
printf("\n"); |
for (int i=1; i<= 10; i++) { |
e1 = removeElement(qp); |
if ( qp->length != 10-i ) { |
printf("Test 3.1 failed, length %d should be %d\n", |
qp->length, 10-i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
if ( e1 != i ) { |
printf("Test 3.2 failed, element %d should be %d\n", |
e1, i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
} |
|
|
/* a fatal test */ |
if ( 0 ) { |
printf("Test 4: remove on empty queue\n"); |
e2 = removeElement(qp); |
tests_failed++; |
} |
|
printf("Tests Passed: %d\n", tests_passed); |
printf("Tests Failed: %d\n", tests_failed); |
} |
int deleteElement(queue *q, int index)
node *tail
field from the queue struct in the linked list queue implementation? addElement
could apply to a queue or an operation on large integers. To distinguish between ADTs one usually adopts some naming convention. A typical one is to prefix the function or procedure with something like q_
to indicate that it belongs to the qeue ADT. Note this is one of the issues that is solved by adopting an object-oriented style of programming. /* |
Queue of int, implemented by a linked list |
|
Names are prefixed with q_ to indicate methods that belong to the |
queue ADT. |
*/ |
#ifndef _queue_h |
#define _queue_h |
|
/* |
A linked list contains nodes which hold the information for |
that node (val) and the pointer to the next node in the |
list (next). |
|
When next is 0 (null pointer) there is no following cell. |
*/ |
typedef struct q_node_t { |
int val; |
struct q_node_t *next; |
} q_node_t; |
|
/* |
The queue is implemented by keeping a pointer to the head |
node of a linked list. To make adding nodes to the queue |
efficient, we also have a pointer to the tail node of the |
list. |
|
A queue is manipulated by passing around a pointer to its |
queue struct. |
*/ |
typedef struct { |
q_node_t *head; |
q_node_t *tail; |
int length; |
} q_instance_t; |
|
/* |
A queue is a pointer to a queue_instance |
*/ |
typedef q_instance_t *queue; |
|
/* |
Constructor: create a queue instance and return a queue |
(i.e. a pointer to the struct). |
*/ |
|
queue q_create(int size_hint); |
|
/* |
Destructor: free up the storage used by the queue. |
*/ |
void q_destroy(queue q); |
|
/* |
Get the current length of the queue |
*/ |
int q_length(queue q); |
|
/* |
Add the element val to the tail of the queue q |
*/ |
void q_add(queue q, int val); |
|
/* |
Remove and return the element at the head of queue q |
|
Pre-condition: q_length(q) > 0 |
*/ |
int q_remove(queue q); |
|
/* |
Get the element of the queue at position index, |
where the head of the queue is position 0, and the |
tail is at position q_length(q)-1 |
*/ |
int q_get_item(queue q, int index); |
|
/* |
Return the element at index in the queue, and deletes it from inside |
the queue. |
*/ |
int q_delete_item(queue q, int index); |
|
#endif |
initializeQueue
with a function q_create
that creates a queue and returns a handle to it. This way you cannot get an uninitialized queue. Also, there is a q_destroy
that frees up all the storage associated with the queue. q_create
is called a constructor and q_destroy
is called a destructor /* non-functional stub code */
/* |
Implementation of queue Abstract Data Type (ADT) |
*/ |
|
#include <stdlib.h> |
#include <stdio.h> |
#include <assert.h> |
#include "queue.h" |
|
/* |
Constructor: create a queue instance and return a queue |
(i.e. a pointer to the struct). |
*/ |
|
queue q_create(int size_hint) { |
queue q = (q_instance_t *) malloc(sizeof(q_instance_t)); |
assert(q != 0); |
|
q->head = 0; |
q->tail = 0; |
q->length = 0; |
|
return q; |
} |
|
/* |
Destructor: free up the storage used by the queue. |
*/ |
void q_destroy(queue q) { |
/* get rid of all the elements in the queue */ |
while ( q_length(q) > 0 ) { |
q_remove(q); |
} |
free(q); |
} |
|
/* |
Get the current length of the queue |
*/ |
int q_length(queue q) { |
return q->length; |
} |
|
/* |
Add the element val to the tail of the queue q |
*/ |
|
void q_add(queue q, int val) { |
q_node_t * n = (q_node_t *) malloc(sizeof(q_node_t)); |
|
// make sure malloc worked |
assert(n != 0); |
|
// initialze the new node to contain the value and have no next |
n->val = val; |
n->next = 0; |
|
// if there is stuff in the queue, add new node to end |
// if an empty queue, need to set up head. |
if (q->length != 0) { |
q->tail->next = n; |
} else { |
q->head = n; |
} |
|
// new node is at the tail |
q->tail = n; |
q->length++; |
} |
|
/* |
Remove and return the element at the head of queue q |
|
Pre-condition: q_length(q) > 0 |
*/ |
|
int q_remove(queue q) { |
assert(q->length > 0); |
|
// get the value at the head |
int rval = q->head->val; |
|
// new head is the next one |
q_node_t *new_head = q->head->next; |
|
/* Now we can free the old head so that we don't leave unused |
memory allocated |
*/ |
free(q->head); |
|
q->length--; |
|
// Now we can set the head to new_head |
q->head = new_head; |
|
// if queue is empty, we need to set tail also. |
if (q->length == 0) { |
q->tail = 0; |
} |
|
return rval; |
} |
|
/* |
Get the element of the queue at position index, |
where the head of the queue is position 0, and the |
tail is at position length(q)-1 |
*/ |
int q_get_item(queue q, int index) { |
assert(0 <= index && index < q->length); |
/* non-functional stub code */ |
return 0; |
} |
|
/* |
Return the element at index in the queue, and deletes it from inside |
the queue. |
*/ |
int q_delete_item(queue q, int index) { |
assert(0 <= index && index < q->length); |
/* non-functional stub code */ |
return 0; |
} |
|
|
#include <stdio.h> |
#include <stdlib.h> |
#include "queue.h" |
|
/* |
print the contents of a queue from head to tail going left |
to right. |
*/ |
void q_printf(queue q) { |
for (int i=0; i < q_length(q); i++) { |
printf("%d ", q_get_item(q, i)); |
} |
} |
|
|
int main(int argc, char *argv[]) { |
/* test harness */ |
int tests_passed = 0; |
int tests_failed = 0; |
|
/* create a queue with a size hint */ |
printf("Creating queue\n"); |
queue qp = q_create(20); |
|
/* how do we see if queue was propery initialized? */ |
|
/* add and remove one element */ |
int e1 = 42; |
|
q_add(qp, e1); |
printf("Test 1: "); |
q_printf(qp); |
printf("\n"); |
|
/* length should have gone up by one */ |
|
if ( q_length(qp) != 1 ) { |
printf("Test 1 failed, length %d should be 1\n", |
q_length(qp)); |
tests_failed++; |
} |
else { |
printf("Test 1 passed.\n"); |
tests_passed++; |
} |
|
|
printf("Test 2: "); |
int e2 = q_remove(qp); |
q_printf(qp); |
printf("\n"); |
|
if ( q_length(qp) != 0 ) { |
printf("Test 2.1 failed, length %d should be 0\n", |
q_length(qp)); |
tests_failed++; |
} |
else { |
printf("Test 2.1 passed.\n"); |
tests_passed++; |
} |
|
if ( e1 != e2 ) { |
printf("Test 2.2 failed, e2 %d should equal e1 %d\n", |
e2, e1); |
tests_failed++; |
} |
else { |
printf("Test 2.2 passed.\n"); |
tests_passed++; |
} |
|
printf("Test 3: "); |
for (int i=1; i <= 10; i++) { |
q_add(qp, i); |
} |
q_printf(qp); |
printf("\n"); |
for (int i=1; i<= 10; i++) { |
e1 = q_remove(qp); |
if ( q_length(qp) != 10-i ) { |
printf("Test 3.1 failed, length %d should be %d\n", |
q_length(qp), 10-i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
if ( e1 != i ) { |
printf("Test 3.2 failed, element %d should be %d\n", |
e1, i); |
tests_failed++; |
} |
else { |
tests_passed++; |
} |
} |
|
|
/* a fatal test */ |
if ( 0 ) { |
printf("Test 4: remove on empty queue\n"); |
e2 = q_remove(qp); |
tests_failed++; |
} |
|
printf("Tests Passed: %d\n", tests_passed); |
printf("Tests Failed: %d\n", tests_failed); |
} |
void q_set_item(queue q, int index, int new_val)
15. Queues Tangible Computing / Version 3.20 2013-03-25 |