Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 44

Chapter XXXVI

Queues

Chapter XXXVI Topics


36.1 Introduction

36.2 Queues at the Abstract Level

36.3 The APQueue Class

36.4 An Integer Queue Implementation

36.5 Queues with “Wrap-Around” Logic

36.6 The Complete APQueue Library

36.7 Worked-Out Exercises

Chapter XXXVI Queues 36.1


36.1 Introduction

Chapter XXXVI will be very similar to the previous chapter. Once again an ADT
will be introduced, the queue, this time. You will first learn what a queue is in a
strictly abstract manner. The queue is implemented for you already as the
apqueue class, and this class will be explored at the interface level. Like the
previous stack ADT, you will then write a program assignment with the inclusion
and availability of a queue ADT. After the program assignment is finished, you
will learn the implementation of a queue and take a close look at the source code
of the provided apqueue class.

This chapter will be shorter than the previous chapter. You are about to learn
about the last AP class, and at this stage you should have an easier time
understanding the source code. The pain-staking, function-by-function approach
that was used with the apvector class will not be repeated here. You will learn
about the implementation of a queue ADT by exploring a limited integer queue
data structure. This is pretty much the approach used with the stack data structure
of chapter XXXV.

The main focus at the implementation level is to see how the queue is different
from the stack. There are some subtle differences that must be understood to
implement the queue properly. At the same time we are doing a little stage setting
here. In later chapters we will return to the stack and the queue and you will find
that there are totally different approaches in implementation. The exact same
ADT can be implemented with radically different program code, and yet the
functionality of the stack and queue is the same.

36.2 Queues at the Abstract Level

Traditionally, stacks and queues are introduced at pretty much the same time.
The two data structures are similar in some ways and different in other ways. The
most significant similarity is how stacks and queues access data. The stack only
allows access at one end of the data structure called the top. You will find that
access to queue data is also at the end of the data structure, but both ends of the
queue data structure are used.
Queue Definition

Chapter XXXVI Queues 36.2


A queue is a data structure with elements of the same
type. Queue elements can only be entered at one end
of the queue, called the back, and removed from the
other end of the queue, called the front, in a First In,
First Out (FIFO) manner.

The queue definition - like the earlier stack definition - includes the key to the queue
data structure, which is called a FIFO in computer science. The queue shares
several features with the stack. Both the stack and the queue are abstract data
structures, which must be implemented with some C++ provided data structure.
Both the stack and queue allow data access only at the end of the data structure, not
anywhere in the middle, like an array. The stack removes elements from the top of
the stack, and the queue removes elements from the front of the queue. You will
learn shortly why a stack uses “top” and a queue uses “front” to indicate the end of
the data structure.

The main difference between stacks and queues is the location where new elements
are entered. The stack keeps on entering new data at the top of the stack. The
queue enters new elements at the back of the stack. This difference seems small,
but an airplane landing analogy shows that the outcome is very significant indeed. If
a queue controls an airplane landing sequence, the airplanes land in the sequence
that they arrive. This is a classic example of a FIFO. A stack data structure for the
same job will be disastrous because the last plane arriving is the first plane to land.
If planes continue to arrive, the first plane that arrived will never land.

What functions are necessary to make a queue practical? Functions, such as Push
and Pop, basically mean add-to and remove-from, but the names push and pop
indicate operations that are peculiar to a stack. We used four essential functions
with stacks, and there are also four comparable, essential queue functions with a few
minor name changes.

EnQueue Definition

EnQueue is a queue operation that adds a new element at


the back end of a queue, which is the opposite end where
queue elements are removed.

DeQueue Definition

Chapter XXXVI Queues 36.3


DeQueue is a queue operation that removes an existing
element from the front end of a queue, which is the opposite
end where queue elements are added.

ClearQueue Definition

ClearQueue is a queue operation that clears a queue of


any existing data. This operation initializes the queue.

MakeEmpty is another popular name for ClearQueue.

EmptyQueue Definition

EmptyQueue is a queue operation that determines if


a queue is empty.

IsEmpty is another popular name for EmptyQueue.

These four queue operations represent the essential operations required to


manipulate a queue data structure. Like the stack, there are additional operations
that will make queue manipulation easier. You can also include operations to
determine the number of elements in a queue (length), look at the next element that
will be dequeued without removing it (front), assign queues, and dequeue elements
without copying them. Like all classes, you can always decide to add additional
functionality as practical use indicates the need for it.

Let us use pictures again to illustrate the concepts. The illustrations used with the
stack in the previous chapter used vertical pictures, which helped to symbolize the
idea of the top of the stack. Queues use the front and the back of the data structure.
This is easier to visualize with a horizontal picture. In the pictures that follow, F
will indicate the front end of the queue, B will indicate the back end of the queue.

Integer Queue after QlearQueue


F B

Chapter XXXVI Queues 36.4


Integer Queue after EnQueue(157)
F 157 B

Integer Queue after EnQueue(999)


F 157 999 B

Integer Queue after EnQueue(500)


F 157 999 500 B

Integer Queue after DeQueue(X)


F 999 500 B

Integer Queue after EnQueue(X)


F 999 500 157 B

Integer Queue after DeQueue(X)


F 500 157 B

Integer Queue after EnQueue(X)


F 500 157 999 B

Integer Queue after EnQueue(X)


F 500 157 999 999 B

Integer Queue after DeQueue(X)


F 157 999 999 B

I am sure that you are observing the curious situation that the queue appears to
have reached one end where new elements are enqueued, while at the same time
showing perfectly good space available in the front. This is an important concern
and hold on to that thought. We shall definitely come back to this issue at a later
point in the chapter.

36.3 The apqueue Class

Chapter XXXVI Queues 36.5


The apqueue class has the same set of functions as the apstack class. The
behavior of the stack’s LIFO and the queue’s FIFO is different, but you will see
that the functions are very similar. The first program, for instance, starts just like
the first stack program by defining two queue objects and checking to see if these
objects are empty.

// PROG3601.CPP
// This program defines two apqueue objects.
// The objects are cleared and checked if they are
empty.

#include <iostream.h>
#include <conio.h>
#include "APQUEUE.H"

void main()
{
clrscr();
// Defining two apqueue objects
apqueue <int> IntQueue;
apqueue <double> RealQueue;
cout << "Two queue objects have been instantiated"
<< endl << endl;
// Clearing the queues
IntQueue.makeEmpty();
RealQueue.makeEmpty();
cout << "All queue objects have been made empty"
<< endl << endl;
// Checking if IntQueue is empty
if (IntQueue.isEmpty())
cout << "IntQueue is empty" << endl << endl;
else
cout << "IntQueue is not empty" << endl << endl;
getch();
}

PROG3601.CPP OUTPUT

Two queue objects have been instantiated

All queue objects have been made empty

IntQueue is empty

Chapter XXXVI Queues 36.6


The stack class and the queue class share the two function identifier names
makeEmpty and isEmpty. These names can and should be the same because
they have the exact same functionality. It is not possible to do this same approach
with the push and pop functions. Items are pushed on top of a stack, and items
can be “popped” off a stack. This same terminology does not make sense with a
queue. The corresponding functions for adding and removing elements with the
apqueue class are called enqueue and dequeue. The next program example,
PROG3602.CPP, shows how to use the enqueue and dequeue member
functions of the apqueue class.

// PROG3602.CPP
// This program shows how to access the queue with the
// enqueue and dequeue functions.

#include <iostream.h>
#include <conio.h>
#include "APQUEUE.H"

void main()
{
clrscr();
int K;
apqueue <int> IntQueue;
IntQueue.makeEmpty();

// Enqueueing new elements to IntQueue


for (K=1; K <= 5; K++)
{
cout << "Enqueueing " << K << " on IntQueue" <<
endl;
IntQueue.enqueue(K);
}
cout << endl;

// Dequeueing and returning elements from IntQueue


while (!IntQueue.isEmpty())
{
IntQueue.dequeue(K);
cout << "Dequeueing " << K << " from Intqueue" <<
endl;

Chapter XXXVI Queues 36.7


}
cout << endl;
getch();
}

PROG3602.CPP OUTPUT

Enqueueing 1 on IntQueue
Enqueueing 2 on IntQueue
Enqueueing 3 on IntQueue
Enqueueing 4 on IntQueue
Enqueueing 5 on IntQueue

Dequeueing 1 from IntQueue


Dequeueing 2 from IntQueue
Dequeueing 3 from IntQueue
Dequeueing 4 from IntQueue
Dequeueing 5 from IntQueue

Function front accesses the front element in a queue without removing the
element. Function length returns the number of elements in the queue. The
length function has the same name and purpose as the corresponding stack
function. Function front has a different name but the purpose is the same as the
top function with the apstack class, which is to look at the next element to be
removed, without actually removing the element. You may think that such a
function is unnecessary because pop and dequeue do the job nicely and you can
look at the element to your hearts desire.

There are some practical situations that call for comparing without removing.
Consider a situation where a certain set of elements need to be removed from the
top of a stack or the front of a queue until a certain value is encountered. Now the
element with the specified value is not supposed to be removed. Identifying this
element requires looking at the next value and both top and front allow a look at
the element while leaving the element neatly tucked in its stack or queue.

Using top and front

Functions top and front allow access to the next element


to be removed from a stack or a queue. This access is

Chapter XXXVI Queues 36.8


done without removing the next element.

Program PROG3603.CPP demonstrates the use of the front function and length
function of the apqueue class. These functions are placed together in one program
because the length function can prove that access to the queue with front has not
altered the number of elements in the queue.

// PROG3603.CPP
// This program demonstrates how to identify the front
element
// of a Queue and how to get the number of Queue
elements.

#include <iostream.h>
#include <conio.h>
#include "APQUEUE.H"

void main()
{
clrscr();
int K;
apqueue <int> IntQueue;
IntQueue.makeEmpty();

// Enqueueing new elements to IntQueue


for (K=1; K <= 5; K++)
{
cout << "Enqueueing " << K << " on IntQueue" <<
endl;
IntQueue.enqueue(K);
}
cout << endl;

// Identifying the front element of IntQueue


K = IntQueue.front();
cout << "The front element of IntQueue is " << K
<< endl << endl;

Chapter XXXVI Queues 36.9


// Determining the number of elements in IntQueue
K = IntQueue.length();
cout << "There are " << K << " elements in IntQueue"
<< endl << endl;

getch();
}

PROG3603.CPP OUTPUT

Enqueueing 1 on IntQueue
Enqueueing 2 on IntQueue
Enqueueing 3 on IntQueue
Enqueueing 4 on IntQueue
Enqueueing 5 on IntQueue

The front element of IntQueue is 1

There are 5 elements in IntQueue

At this stage you realize that an assignment operator function is pretty much
standard procedure for any class declaration. The queue is no different and will
need its own assignment operator. The next program assignment also provides a
second, overloaded, dequeue function. The second function also removes
elements from the front of the queue object, but these elements will not be copied
to any variable. Program PROG3604.CPP demonstrates these two functions.

// PROG3604.CPP
// This program demonstrates queue assignment and also
shows a
// second dequeue function that does not return any
queue element.

#include <iostream.h>
#include <conio.h>
#include "APQUEUE.H"

Chapter XXXVI Queues 36.10


void main()
{
clrscr();
int K;
// Creating queue variables
apqueue <int> IntQueue1;
apqueue <int> IntQueue2;

// Clearing the queues


IntQueue1.makeEmpty();
IntQueue2.makeEmpty();

// Enqueueing new elements to IntQueue1


for (K=1; K <= 5; K++)
{
cout << "Enqueueing " << K << " on IntQueue1" <<
endl;
IntQueue1.enqueue(K);
}
cout << endl;

// Assigning Intqueue1 to Intqueue2


IntQueue2 = IntQueue1;
cout << "IntQueue1 has been assigned to IntQueue2"
<< endl << endl;

// Dequeueing and not returning elements from


IntQueue1
IntQueue1.dequeue();
K = IntQueue1.length();
cout << "Dequeueing the front element from
IntQueue1." << endl;
cout << "There are now " << K << " elements in
IntQueue1"
<< endl << endl;
getch();
}

PROG3604.CPP OUTPUT

Enqueueing 1 on IntQueue1

Chapter XXXVI Queues 36.11


Enqueueing 2 on IntQueue1
Enqueueing 3 on IntQueue1
Enqueueing 4 on IntQueue1
Enqueueing 5 on IntQueue1

IntQueue1 has been assigned to IntQueue2

Dequeueing the front element from IntQueue1


There are now 4 elements in IntQueue1

We will finish the apqueue class introduction by using typedef to create a special
QueueType that is convenient to use for parameter passing purposes. The
apqueue class has been presented with less explanation than the previous classes.
I am making a basic assumption that students are seeing patterns and have
acquired more knowledge and confidence with using these provided AP classes.

APCS Exam Alert

The apstring, apvector, apmatrix, apstack and apqueue


classes offer dual assistance for students preparing for the
APCS examination.

First, these classes provide data type types that will make
program development simpler in C++. Students are
expected to make efficient use of the AP classes.

Second, the implementations of the AP classes provide


students with excellent examples of C++ program code
and program style.

Chapter XXXVI Queues 36.12


Program PROG3605.CPP is the last program example for the apqueue class,
which demonstrates parameter passing. This officially concludes the introduction
of the provided AP classes. It does not conclude the introduction of a variety of
abstract data types that will be investigated in chapters to come.

// PROG3605.CPP
// This program demonstrates how to use the apqueue
class with
// functions and parameter passing.

#include <iostream.h>
#include <conio.h>
#include "APQUEUE.H"

typedef apqueue <int> QueueType;

void FillQueue(QueueType &Q);


void EmptyQueue(QueueType &Q);

void main()
{
clrscr();
QueueType MyQueue;
FillQueue(MyQueue);
EmptyQueue(MyQueue);
getch();
}

void FillQueue(QueueType &Q)


{
int K;
Q.makeEmpty();
for (K=1; K<=9; K++)
{
Q.enqueue(K);
cout << "Enqueueing " << K << " on the queue" <<
endl;
}
cout << endl << endl;
}

Chapter XXXVI Queues 36.13


void EmptyQueue(QueueType &Q)
{
int K;
while (!Q.isEmpty())
{
Q.dequeue(K);
cout << "Dequeueing " << K << " from the queue"
<< endl;
}
}

PROG3605.CPP OUTPUT

Enqueueing 1 on the queue


Enqueueing 2 on the queue
Enqueueing 3 on the queue
Enqueueing 4 on the queue
Enqueueing 5 on the queue
Enqueueing 6 on the queue
Enqueueing 7 on the queue
Enqueueing 8 on the queue
Enqueueing 9 on the queue

Dequeueing 1 from the queue


Dequeueing 2 from the queue
Dequeueing 3 from the queue
Dequeueing 4 from the queue
Dequeueing 5 from the queue
Dequeueing 6 from the queue
Dequeueing 7 from the queue
Dequeueing 8 from the queue
Dequeueing 9 from the queue

You are now expected to follow the same pattern that was established during the
last chapter. At this stage you have learned the interface with the apqueue class.
You need to concentrate on assignment LAB36A, which uses the apqueue class
before continuing with the implementations of a queue class.

This assignment will use a program that you have created earlier and enhance it
with the use of the apqueue class. The object is to create a screen saver of the
type that moves around the screen and erases itself.

Chapter XXXVI Queues 36.14


The queue data structure is ideal for such a program. The coordinates of the
graphics objects that are drawn can be stored in a queue and after a predetermined
number of objects are displayed the queue is used to erase the earlier graphics
object in the precise order that they are displayed. Detailed information will be
provided with the provided lab assignment.

Summary of apqueue Functions

Defining an apqueue object:


apqueue <int> IntQueue;

Clearing a queue of any elements:


IntQueue.makeEmpty();

Checking if a queue is empty:


if (IntQueue.isEmpty())

Add a new element to the queue:


IntQueue.enqueue(QueueItem);

Removing a queue element, and storing the element:


IntQueue.dequeue(QueueItem);

Removing a queue element without storing the item:


IntQueue.dequeue();

Determining the size of a queue:

Chapter XXXVI Queues 36.15


N = IntQueue.length();

Accessing front queue element without dequeueing:


QueueItem = InQueue.front();

Queue Assignment:
IntQueue1 = IntQueue2;

36.4 An Integer Queue Implementation

The implementation of a queue class shows that it is easy to be hasty with


program development. A quick implementation without careful thought about the
implementation and thorough testing can cause some interesting errors. Consider
the next program example. It shows great similarity to the integer stack class of
the previous chapter. The four essential queue functions are implemented in
pretty much the same manner as the stack functions. The only difference is the
manner in which queue elements are added and removed from the data structure.
The data structure to store data for the queue uses struct like the stack. An array
stores the queue elements, and two additional fields store the values of the Front
and Back location of the queue. All of this seems innocent enough and the next
program example demonstrates that the queue implementation works in the
manner that is expected.

// PROG3606.CPP
// This program implements the basic queue functions,
// MakeEmpty, IsEmpty, EnQueue, and DeQueue an integer
queue.

#include <iostream.h>
#include <conio.h>
#include "BOOL.H"

struct Queue

Chapter XXXVI Queues 36.16


{
int Front; // indicates front of
the queue
int Back; // indicates back of
the queue
int Elements[100]; // storage for 100
queue items
};

void MakeEmpty(Queue &Q);


bool IsEmpty(const Queue &Q);
void EnQueue(Queue &Q, int Item);
void DeQueue(Queue &Q, int &Item);

void main()
{
Queue Q;
int K;
MakeEmpty(Q);
for (K = 100; K < 1000; K+=100)
{
cout << "Enqueueing " << K << " on the queue" <<
endl;
EnQueue(Q,K);
}
cout << endl;
while (!IsEmpty(Q))
{
DeQueue(Q,K);
cout << "DeQueueing " << K << " from the queue"
<< endl;
}
}
///////////////////////////////////////////////////////
///////////
// FUNCTION
IMPLEMENTATIONS ////////////////////////////////////
///////////////////////////////////////////////////////
///////////

void MakeEmpty(Queue &Q)


// description: empties queue of any elements
// postcondition: Q is empty
{
Q.Back = -1;
Q.Front = 0;

Chapter XXXVI Queues 36.17


}

bool IsEmpty(const Queue &Q)


// description: checks is Q is empty
// postcondition: returns true if Q is empty and false
otherwise
{
return Q.Front > Q.Back;
}

void EnQueue(Queue &Q, int Item)


// description: adds a new element to the back of Q
// precondition: Q = e1, e2...en
// postcondition: Q = e1, e2...en, Item
{
Q.Back++;
Q.Elements[Q.Back] = Item;
}

void DeQueue(Queue &Q, int &Item)


// description: removes the top element from Q
// precondition: Q = e1, e2...en
// postcondition: Q = e1, e2...e(n-1)
{
Item = Q.Elements[Q.Front];
Q.Front++;
}

PROG3606.CPP OUTPUT

Enqueueing 100 on the queue


Enqueueing 200 on the queue
Enqueueing 300 on the queue
Enqueueing 400 on the queue
Enqueueing 500 on the queue
Enqueueing 600 on the queue
Enqueueing 700 on the queue
Enqueueing 800 on the queue
Enqueueing 900 on the queue

Dequeueing 100 from the queue


Dequeueing 200 from the queue
Dequeueing 300 from the queue
Dequeueing 400 from the queue
Dequeueing 500 from the queue
Dequeueing 600 from the queue

Chapter XXXVI Queues 36.18


Dequeueing 700 from the queue
Dequeueing 800 from the queue
Dequeueing 900 from the queue
The previous program example is an example of limited testing of a new product.
As it stood, the program works fine and the queue functions appear to do their
intended job. The story changes with the next program. The same logic will be
used for implementing the queue functions in the next program example.
However, this program declares a queue class with nice, private data and proper,
public member functions. Our trusty apvector is used to store the queue data.

Program PROG3607.CPP will be tested differently than the previous program to


illustrate a very serious problem with the implementation of our EnQueue and
DeQueue functions. I switched to a Queue class with apvector storage not only
because it is a better queue design, but it also helps to illustrate the flaws of our
functions better. The simple truth is that this program is going to crash. With the
previous program that uses a C-style array, the program will probably freeze the
computer execution or do something else that is unpredictable. Now that we are
using apvector to store queue elements, the output will still abort, but it will do so
with helpful error messages.

The essence of the program execution is that a queue is constructed with space for
five queue elements. Four items are enqueued, followed by dequeueing three
items. This leaves one queue item and space for four more; at least so it seems.
The program attempts to enqueue four new items, and there is a crash problem.

// PROG3607.CPP
// This program creates a Queue class and demonstrates
the
// problem with a queue data structure that is
implemented with
// some linear list.

#include <iostream.h>
#include <conio.h>
#include "BOOL.H"
#include "APVECTOR.H"

class Queue
{
private:
int Size;
int Front;
int Back;
apvector <int> Elements;

Chapter XXXVI Queues 36.19


public:
Queue(int N);
void MakeEmpty();
bool IsEmpty() const;
void EnQueue(int Item);
void DeQueue(int &Item);
};

void AddToQueue(Queue &Q, int N)


{
cout << endl;
for (int K = 1; K <= N; K++)
{
cout << "Enqueueing " << K*100 << " on the queue"
<< endl;
Q.EnQueue(K*100);
}
}

void RemoveFromQueue(Queue &Q, int N)


{
cout << endl;
int Item;
for (int K = 1; K <= N; K++)
{
Q.DeQueue(Item);
cout << "Dequeueing " << Item << " from the
queue" << endl;
}
}

void main()
{
Queue Q(5);
int K;
clrscr();
Q.MakeEmpty();
AddToQueue(Q,4);
RemoveFromQueue(Q,3);
AddToQueue(Q,4);
cout << endl;
while (!Q.IsEmpty())
{

Chapter XXXVI Queues 36.20


Q.DeQueue(K);
cout << "DeQueueing " << K << " from the queue"
<< endl;
}
getch();
}

///////////////////////////////////////////////////////
///////////
// FUNCTION
IMPLEMENTATIONS ////////////////////////////////////
///////////////////////////////////////////////////////
///////////

Queue::Queue(int N)
// description: constructor
// postcondition: object is constructed with space for
N elements
: Size(0), Front(0), Back(-1), Elements(N)
{
}

void Queue::MakeEmpty()
// description: empties queue of any elements
// postcondition: Queue object is empty
{
Size = 0;
Back = -1;
Front = 0;
}
bool Queue::IsEmpty() const
// description: checks if Queue object is empty
// postcondition: returns true if Queue object is
empty and
// false otherwise
{
return Size == 0;
}

void Queue::EnQueue(int Item)


// description: adds Item to the back of a Queue
object
// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...en, Item
{

Chapter XXXVI Queues 36.21


Back++;
Elements[Back] = Item;
Size++;
}

void Queue::DeQueue(int &Item)


// description: removes the front element from a
Queue object
// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...e(n-1)
{
Item = Elements[Front];
Front++;
Size--;
}

PROG3607.CPP OUTPUT

Enqueueing 100 on the queue


Enqueueing 200 on the queue
Enqueueing 300 on the queue
Enqueueing 400 on the queue

Dequeueing 100 from the queue


Dequeueing 200 from the queue
Dequeueing 300 from the queue

Enqueueing 100 on the queue


Enqueueing 200 on the queue
Illegal vector index: 5 max index = 4
Abnormal program termination

The program execution ends with abnormal program termination and some
indication that the vector index is not the correct size. This is clear evidence that
something fishy is happening and our nifty queue data structure is not as finished
as we may think it is. The problem can be seen by going back to the pictures that
were first used to illustrate an abstract queue. The diagrams will use a queue with
five spaces and perform the same actions that the previous sample program
execution demonstrated.

Integer Queue after constructing new object with five spaces


F B

Chapter XXXVI Queues 36.22


Integer Queue after four calls to EnQueue()
F 100 200 300 400 B

The queue shows that there is now one space left after enqueueing four elements.
The next action of our program is to remove three queue elements. Now make sure
that you realize the nature of a queue. New elements are enqueued at the back and
existing elements are dequeued from the front.

Integer Queue after three calls to DeQueue()


F 400 B

The picture of the queue accurately shows the manner in which our queue is
implemented. We are looking at four available spaces in the queue. The problem is
that new elements are added at the back of the queue, and at that location there is
only one space available. The implementation of our EnQueue function has no
capability of accessing the three empty spaces at the front of the queue.

One item is enqueued without any problem in the 5th queue space, which happens to
be at index 4, the maximum index of the Elements array. Any attempt to add any
more items to this queue will cause an index error for the apvector object and the
program will terminate.

36.5 Queues With “Wrap-Around” Logic

The problem with the queue implementation of the previous section is that the
Front and Back of the queue are fixed at the actual front and back of the queue.
This system guarantees that you will run out of space with repeated enqueue and
dequeue operations. In fact, it is possible that the queue is completely empty and
the Back pointer is all the way at the back end of the queue, which means that
there is no space left to add new queue elements.

The solution to this problem is to create a virtual Front and virtual Back with
“wrap-around” logic. When the Back reaches the end of the queue, it moves

Chapter XXXVI Queues 36.23


around to the front of the queue and checks if there is space at that location. The
Front pointer uses the same logic and chases the Back pointer. In reality, values
are stored in a linear memory space. In a logical, abstract sense, the queue has
become a circular space that no longer has any physical ends. Check out program
PROG3608.CPP and see if the different function implementations make sense.

// PROG3608.CPP
// This program uses "wrap-around" logic to solve the
problem
// of working with a strictly "linear" implementation
for
// queue storage.

#include <iostream.h>
#include <conio.h>
#include "BOOL.H"
#include "APVECTOR.H"

class Queue
{
private:
int Size;
int Front;
int Back;
apvector <int> Elements;
public:
Queue(int N);
void MakeEmpty();
bool IsEmpty() const;
void EnQueue(int Item);
void DeQueue(int &Item);
};

void AddToQueue(Queue &Q, int N)


{
cout << endl;
for (int K = 1; K <= N; K++)
{
cout << "Enqueueing " << K*100 << " on the queue"
<< endl;
Q.EnQueue(K*100);

Chapter XXXVI Queues 36.24


}
}

void RemoveFromQueue(Queue &Q, int N)


{
cout << endl;
int Item;
for (int K = 1; K <= N; K++)
{
Q.DeQueue(Item);
cout << "Dequeueing " << Item << " from the
queue" << endl;
}
}

void main()
{
Queue Q(5);
int K;
clrscr();
Q.MakeEmpty();
AddToQueue(Q,4);
RemoveFromQueue(Q,3);
AddToQueue(Q,4);
cout << endl;
while (!Q.IsEmpty())
{
Q.DeQueue(K);
cout << "DeQueueing " << K << " from the queue"
<< endl;
}
getch();
}

///////////////////////////////////////////////////////
///////////
// FUNCTION
IMPLEMENTATIONS ////////////////////////////////////
///////////////////////////////////////////////////////
///////////

Chapter XXXVI Queues 36.25


Queue::Queue(int N)
// description: constructor
// postcondition: object is constructed with space for
N elements
: Size(0), Front(0), Back(-1), Elements(N)
{
}

void Queue::MakeEmpty()
// description: empties queue of any elements
// postcondition: Queue object is empty
{
Size = 0;
Back = -1;
Front = 0;
}

bool Queue::IsEmpty() const


// description: checks if Queue object is empty
// postcondition: returns true if Queue object is
empty and
// false otherwise
{
return Size == 0;
}

void Queue::EnQueue(int Item)


// description: adds Item to the back of a Queue
object
// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...en, Item
{
if (Back == Elements.length()-1)
Back = 0;
else
Back++;
Elements[Back] = Item;
Size++;
}

void Queue::DeQueue(int &Item)


// description: removes the front Item from a Queue
object

Chapter XXXVI Queues 36.26


// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...e(n-1)
{
Item = Elements[Front];
if (Front == Elements.length()-1)
Front = 0;
else
Front++;
Size--;
}

PROG3608.CPP OUTPUT

Enqueueing 100 on the queue


Enqueueing 200 on the queue
Enqueueing 300 on the queue
Enqueueing 400 on the queue

Dequeueing 100 from the queue


Dequeueing 200 from the queue
Dequeueing 300 from the queue

Enqueueing 100 on the queue


Enqueueing 200 on the queue
Enqueueing 300 on the queue
Enqueueing 400 on the queue

Dequeueing 400 from the queue


Dequeueing 100 from the queue
Dequeueing 200 from the queue
Dequeueing 300 from the queue
Dequeueing 400 from the queue

The first thing to realize is that the program works correctly now without any
problems or strange program terminations. The “wrap-around” logic seems to be
doing the trick. Let us look at both the Enqueue and DeQueue functions in turn
and see what is happening.

void Queue::EnQueue(int Item)


// description: adds Item to the back of a Queue
object
// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...en, Item
{

Chapter XXXVI Queues 36.27


if (Back == Elements.length()-1)
Back = 0;
else
Back++;
Elements[Back] = Item;
Size++;
}

The secret of the “wrap-around” logic is to check the index of the Elements array.
The largest - legally allowed - index is one less than the size of the array object.
Function DeQueue first checks if Back is already the largest allowable index, and
if it is, the index value starts over at zero. At this stage, Back appears in front of
Front. However, it does not matter because we have created a circular type of
storage facility that does not recognize the normal front and back of a linear
structure. We are now dealing with a virtual front and back.

The same “wrap-around” logic is used for the DeQueue function. There is a
difference because the DeQueue function first removes the queue item and then
moves the Front pointer. Wherever the Front pointer is located, that is where the
queue element is removed. After the element removal the pointer needs to be
incremented, and if Front is at the end, it will wrap around to the zero index in
precisely the same manner as the EnQueue function.

void Queue::DeQueue(int &Item)


// description: removes the front Item from a Queue
object
// precondition: Queue object = e1, e2...en
// postcondition: Queue object = e1, e2...e(n-1)
{
Item = Elements[Front];
if (Front == Elements.length()-1)
Front = 0;
else
Front++;
Size--;
}

Chapter XXXVI Queues 36.28


36.6 The Complete apqueue Library

In previous AP class chapters we looked at an integer class using a limited


function set first. Then we moved up to a templated class, still using a limited
function set, and then we printed the complete AP class library with every
available function. Well, I have now such confidence in your ability that I am
skipping the limited templated class section, and we will jump straight into the
apqueue library. You will find that the apqueue class uses “wrap-around” logic,
but it is done with the aid of a special help function.

Studying the APQUEUE.H header file helps you to understand the interface
better in using the apqueue library. Studying the APQUEUE.CPP
implementation file will give you a better understanding of queue function logic
and it will also provide examples of well written C++ code.

APQUEUE.H File
//////////////////////////////////////////////////////////////////////////
// APQUEUE.H ///////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Downloaded and altered 04-21-99 by Leon Schram
//
// This is the APQUEUE header file.
// The APQUEUE implementation file must be included at the end of
// this file before #endif.
//
//////////////////////////////////////////////////////////////////////////

// ***********************************************************************
// Last Revised: 8-14-98
//
// - commented out the #include "bool.h", dhj
// - updated comments for constructor/descructor, dhj
//
// APCS queue class
// ***********************************************************************

#ifndef _APQUEUE_H
#define _APQUEUE_H

#include <stdlib.h>
#include "BOOL.H"
#include "APVECTOR.H"

template <class itemType>


class apqueue
{
public:

// constructors/destructor

apqueue( ); // construct empty queue

Chapter XXXVI Queues 36.29


apqueue( const apqueue & q ); // copy constructor
~apqueue( ); // destructor
// assignment

const apqueue & operator = ( const apqueue & rhs );

// accessors

const itemType & front( ) const; // return front (no dequeue)


bool isEmpty( ) const; // return true if empty else false
int length( ) const; // return number of elements in queue

// modifiers

void enqueue( const itemType & item ); // insert item (at rear)
void dequeue( ); // remove first element
void dequeue( itemType & item ); // combine front and dequeue
void makeEmpty( ); // make queue empty

private:

int mySize; // # of elts currently in queue


int myFront; // index of first element
int myBack; // index of last element
apvector<itemType> myElements; // internal storage for elements

// private helper functions


void DoubleQueue(); // double storage for myElements
void Increment(int & val) const; // add one with wraparound
};

// *******************************************************************
// Specifications for queue functions
//
// Any violation of a function's precondition will result in an error message
// followed by a call to exit.
//
// constructors/destructor
//
// apqueue( )
// postcondition: the queue is empty
//
// apqueue( const apqueue & q )
// postcondition: queue is a copy of q
//
// ~apqueue( )
// postcondition: queue is destroyed
//
// assignment
//
// const apqueue & operator = ( const apqueue & rhs )
// postcondition: normal assignment via copying has been performed
//
// accessors
//
// const itemType & front( ) const
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postcondition: returns e1
//
// bool isEmpty( ) const
// postcondition: returns true if queue is empty, false otherwise
//
// int length( ) const
// precondition: queue is [e1, e2, ..., en] with n >= 0
// postcondition: returns n
//
// modifiers:
//
// void enqueue( const itemType & item )
// precondition: queue is [e1, e2, ..., en] with n >= 0

Chapter XXXVI Queues 36.30


// postcondition: queue is [e1, e2, ..., en, item]
//
// void dequeue( )
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postcondition: queue is [e2, ..., en]
//
// void dequeue( itemType & item )
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postcondition: queue is [e2, ..., en] and item == e1
//
// void makeEmpty( )
// postcondition: queue is empty
//
// Examples for use:
//
// apqueue<int> iqueue; // creates empty queue of integers
// apqueue<double> dqueue // creates empty queue of doubles

#include "APQUEUE.CPP"

#endif

APQUEUE.CPP File
//////////////////////////////////////////////////////////////////////////
// APQUEUE.CPP /////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Downloaded and altered 04-21-99 by Leon Schram
//
// This is the APQUEUE implementation file.
// The APQUEUE implementation file must be included at the end of
// the APQUEUE.H header file before #endif.
//
//////////////////////////////////////////////////////////////////////////

// *******************************************************************
// Last Revised: 8/14/98
//
// APCS queue class IMPLEMENTATION
//
// queue implemented using the APCS vector class
// based on queue class in Mark Weiss' : Algorithms, Data Structures,
// and Problem Solving with C++
// *******************************************************************

const int QDEFAULT_SIZE = 10; // default initial queue size

template <class itemType>


apqueue<itemType>::apqueue()
: mySize(0),
myFront(0),

Chapter XXXVI Queues 36.31


myBack( -1 ),
myElements(QDEFAULT_SIZE)

// postcondition: the queue is empty


{

template <class itemType>


apqueue<itemType>::apqueue(const apqueue<itemType> & q)
: mySize(q.mySize),
myFront(q.myFront),
myBack(q.myBack),
myElements(q.myElements)

// postcondition: queue is a copy of q


{

template <class itemType>


apqueue<itemType>::~apqueue()
// postcondition: queue is destroyed
{
// vector destructor takes care of memory
}

template <class itemType>


const apqueue<itemType> &
apqueue<itemType>::operator=(const apqueue<itemType> & rhs)
// postcondition: normal assignment via copying has been performed
{
if( this != &rhs)
{
mySize = rhs.mySize; // copy all fields of rhs
myElements.resize(rhs.myElements.length()); // resize storage
myFront = 0;
myBack = mySize - 1; // index from 0 .. mySize - 1

int k;
int rhsk = rhs.myFront;

for(k=0; k < mySize; k++)


{
myElements[k] = rhs.myElements[rhsk];
Increment(rhsk);
}
}
return *this;
}

template <class itemType>


const itemType &
apqueue<itemType>::front() const
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postcondition: returns e1
{
return myElements[myFront];
}

template <class itemType>


bool apqueue<itemType>::isEmpty() const
// postcondition: returns true if queue is empty, false otherwise
{
return mySize == 0;
}

Chapter XXXVI Queues 36.32


template <class itemType>
int apqueue<itemType>::length() const
// precondition: queue is [e1, e2, ..., en] with n >= 0
// postcondition: returns n
{
return mySize;
}

template <class itemType>


void apqueue<itemType>::enqueue(const itemType & item)
// precondition: queue is [e1, e2, ..., en] with n >= 0
// postcondition: queue is [e1, e2, ..., en, item]
{
if (mySize >= myElements.length()) // grow if necessary to add element
{
DoubleQueue();
}

Increment(myBack); // add element at back of queue


myElements[myBack] = item;
mySize++;
}

template <class itemType>


void apqueue<itemType>::dequeue()
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postconditions: queue is [e2, ..., en] and item == e1
{
if (isEmpty())
{
cerr << "dequeue from empty queue" << endl;
exit(1);
}

mySize--; // one fewer element


Increment(myFront);
}

template <class itemType>


void apqueue<itemType>::dequeue(itemType & item)
// precondition: queue is [e1, e2, ..., en] with n >= 1
// postcondition: queue is [e2, ..., en] and item == e1
{
if (isEmpty())
{
cerr << "dequeue from empty queue" << endl;
exit(1);
}
item = myElements[myFront];
mySize--; // one fewer element
Increment(myFront);
}

template <class itemType>


void apqueue<itemType>::makeEmpty()
// postcondition: queue is empty
{
mySize = 0;
myFront = 0;
myBack = -1;
}

Chapter XXXVI Queues 36.33


template <class itemType>
void apqueue<itemType>::Increment( int & val ) const
// postcondition: val increased by one relative to size of myElements
// i.e., wraps to 0 after reaching capacity of vector storage
{
val++;
if (val >= myElements.length() )
{ val = 0;
}
}

template <class itemType>


void apqueue<itemType>::DoubleQueue()
// precondition: queue = e1, e2, ..., en, size = n, capacity = m
// postcondition: queue = e1, e2, ..., en, size = n, capacity = 2*m
{
// this could be made more efficient by doing the copy
// in place (without the temporary vector temp)

apvector<itemType> temp(myElements.length()*2); // new storage


int j,k=myFront; // copy to 0..
for(j=0; j < mySize; j++)
{
temp[j] = myElements[k];
Increment(k);
}
myElements = temp; // reset private vars to mirror new storage
myFront = 0;
myBack = mySize-1;
}

Chapter XXXVI Queues 36.34


36.7 Worked-Out Exercises

All the exercise examples are multiple choice questions. There is a temptation to
"eliminate" impossible answers and use various other tricks that work well with
multiple choice questions. In computer science "multiple choice test" tricks are
usually not a good approach. Take each problem, compute the solutions, as and
then match your solution. The reasoning for each question is not provided. You
are given the correct answer. It is your job to determine how to derive the correct
answer.

Questions 1-4 refer to the following function prototypes and program segment.
Assume that any other necessary declarations and definitions are made properly.

void AddTo(Type &T; int X);


// postcondition: Integer X is added to T in a manner
that is
// appropriate for the abstract definition of T.

void Remove(Type &T; int &X);


// postcondition: Integer X is removed from T in a
manner that is
// appropriate for the abstract definition of T.

A = 12;
B = 20;
C = 25;
AddTo(S,A);
AddTo(S,B);
AddTo(S,C);
Remove(S,D);
Remove(S,E);

Chapter XXXVI Queues 36.35


F = D + E;
AddTo(S,F);
Remove(S,D);
Remove(S,E);

Exercise 01

If Type is a stack, what is the value of D?

(A) 25
(B) 20
(C) 12
(D) 45
(E) 32

Answer: D

Exercise 02.

If Type is a stack, what is the value of E?

(A) 25
(B) 20
(C) 12
(D) 45
(E) 32

Answer: C

Exercise 03.

If Type is a queue, what is the value of D?

(A) 25
(B) 20
(C) 12
(D) 45
(E) 32

Chapter XXXVI Queues 36.36


Answer: A

Exercise 04.

If Type is a queue, what is the value of E?

(A) 25
(B) 20
(C) 12
(D) 45
(E) 32

Exercise: E

Exercise 05.

Consider the following sequence of program statements. Assume that an apstack


object, S,
is correctly instantiated with apstack functions, and integers A and B are defined.

S.push(A);
S.push(B);
S.pop(C);
A = A + B;
S.push(A);
S.push(C);
S.push(B);
B = B + C;
S.push(B);
while(!S.emptyStack())
{
S.pop(A);

Chapter XXXVI Queues 36.37


cout << A << ” ”;
}

If initially the S is empty, A = 3 and B = 5, then what is the output of the


segment?

(A) 10 5 5 8 3
(B) 3 8 5 5 10
(C) 5 8 3 5 8
(D) 8 5 3 8 5
(E) 5 8 3 8 5

Answer: A

Exercise 06.

Consider the following sequence of program statements. Assume that an apqueue


object, Q,
is correctly instantiated with apqueue functions, and integers A and B are defined.

Chapter XXXVI Queues 36.38


Q.enque(A);
Q.enque(B);
Q.deque(C);
A = A + B;
Q.enque(A);
Q.enque(C);
Q.enque(B);
B = B + C;
Q.enque(B);
while(!Q.emptyQue())
{
Q.deque(A);
cout << A << ” ”;
}

If initially Q is empty, A = 3 and B = 5, then what is the output of the segment?

(A) 10 5 5 8 3
(B) 3 8 5 5 10
(C) 5 8 3 5 8
(D) 8 5 3 8 5
(E) 5 8 3 8 5

Answer: C

Chapter XXXVI Queues 36.39


Exercise 07.

Consider the following function.

void QueueMystery(apqueue <apstring> &Q, apstring Name)


// precondition: Q is a non-empty queue.
// Exactly one element in S == Name.
{
apqueue <apstring> Temp;
apstring Item;
bool Done = false;
Temp.makeEmpty();
while (!Done)
{
Q.dequeue(Item);
Done = Item == Name;
if (!Done)
Temp.enqueue(Item);
}
while (!Temp.isEmpty())
{
Temp.dequeue(Item);
Q.enqueue(Item);
}
}

Assume that Q contains the following names prior to the call to


QueueMystery.
Kathy Thomas Susan Oscar

[Front]

Which of the following represents the contents of Q after a call to


QueueMystery("Susan")?

(A) Kathy, Thomas, Oscar, Elmo


(B) Kathy, Thomas, Susan, Oscar, Elmo, Kathy, Thomas
(C) Thomas, Kathy, Elmo, Oscar, Susan, Thomas, Kathy
(D) Elmo, Oscar, Susan, Thomas, Kathy
(E) Elmo, Oscar, Thomas, Kathy

Chapter XXXVI Queues 36.40


Answer: B

Exercise 08.

Consider the following function.

void QueueMystery(apqueue <apstring> &Q, apstring Name)


//precondition: Q is a non-empty queue.
{
apqueue <apstring> Temp;
apstring Item;
Temp.makeEmpty();
while (!Q.isEmpty())
{
Q.dequeue(Item);
if (Item != Name)
Temp.enqueue(Item);
}
while (!Temp.isEmpty())
{
Temp.dequeue(Item);
Q.enqueue(Item);
}
}

Assume that Q contains the following names prior to the call to QueueMystery.

Apple Pear Banana Orange Peach Plum

[Front]

Chapter XXXVI Queues 36.41


Which of the following represents the contents of Q after a call to
QueueMystery("Peach")?

(A) Apple, Pear, Banana, Orange, Peach, Plum, Mango


(B) Apple, Pear, Banana, Orange, Peach, Plum, Mango, Peach
(C) Apple, Pear, Banana, Orange, Apple, Pear, Banana, Orange, Plum,
Mango
(D) Mango, Plum, Orange, Banana, Pear, Apple
(E) Apple, Pear, Banana, Orange, Plum, Mango

Answer: E

Exercise 09.

A stack data structure, designed to store integers, has the following operations
available.

InputPush - Gets an integer from the input file and pushes the number
InputPrint - Gets an integer from the input file and prints the
number
PopPrint - Pops an integer from the stack and prints the number
AddPush - Pops two integers from the top of the stack, adds them,
and pushes the sum.

If the input file has the sequence of integers 5, 10, 15, 20, 25 which of the
following number sequences can be printed by the given operations?

I. 35, 25, 10, 5

Chapter XXXVI Queues 36.42


II. 15, 25, 30, 5
III. 5, 25, 20, 25, 10
IV. 25, 35, 15

(A) I and II only


(B) II and III only
(C) II and IV only
(D) I, III and IV only
(E) I, II and IV only

Answer: E

Exercise 10.

A stack data structure, designed to store integers, has the following operations
available.

InputPush - Gets an integer from the input file and pushes the number
InputPrint - Gets an integer from the input file and prints the
number

Chapter XXXVI Queues 36.43


PopPrint - Pops an integer from the stack and prints the number
AddPush - Pops two integers from the top of the stack, adds them,
and pushes the sum.

If the input file has the sequence of integers 30, 60, 90, 120, 150, which of the
following number sequences cannot be printed by the given operations?

(A) 150, 120, 90, 60, 30


(B) 30, 60, 90, 120, 150
(C) 60, 120, 150, 90, 30
(D) 150, 210, 60, 30
(E) 30, 150, 60, 90, 120

Answer: E

Chapter XXXVI Queues 36.44

You might also like