Professional Documents
Culture Documents
Chapter XXXVI
Chapter XXXVI
Queues
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.
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
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
DeQueue Definition
ClearQueue Definition
EmptyQueue Definition
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.
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.
// 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
IntQueue is empty
// 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();
PROG3602.CPP OUTPUT
Enqueueing 1 on IntQueue
Enqueueing 2 on IntQueue
Enqueueing 3 on IntQueue
Enqueueing 4 on IntQueue
Enqueueing 5 on 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.
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();
getch();
}
PROG3603.CPP OUTPUT
Enqueueing 1 on IntQueue
Enqueueing 2 on IntQueue
Enqueueing 3 on IntQueue
Enqueueing 4 on IntQueue
Enqueueing 5 on 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"
PROG3604.CPP OUTPUT
Enqueueing 1 on 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.
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.
// 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"
void main()
{
clrscr();
QueueType MyQueue;
FillQueue(MyQueue);
EmptyQueue(MyQueue);
getch();
}
PROG3605.CPP OUTPUT
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.
Queue Assignment:
IntQueue1 = IntQueue2;
// 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
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 ////////////////////////////////////
///////////////////////////////////////////////////////
///////////
PROG3606.CPP OUTPUT
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;
void main()
{
Queue Q(5);
int K;
clrscr();
Q.MakeEmpty();
AddToQueue(Q,4);
RemoveFromQueue(Q,3);
AddToQueue(Q,4);
cout << endl;
while (!Q.IsEmpty())
{
///////////////////////////////////////////////////////
///////////
// 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;
}
PROG3607.CPP OUTPUT
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.
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.
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.
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
// 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 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 ////////////////////////////////////
///////////////////////////////////////////////////////
///////////
void Queue::MakeEmpty()
// description: empties queue of any elements
// postcondition: Queue object is empty
{
Size = 0;
Back = -1;
Front = 0;
}
PROG3608.CPP OUTPUT
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.
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.
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"
// constructors/destructor
// accessors
// 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:
// *******************************************************************
// 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
#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++
// *******************************************************************
int k;
int rhsk = rhs.myFront;
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.
A = 12;
B = 20;
C = 25;
AddTo(S,A);
AddTo(S,B);
AddTo(S,C);
Remove(S,D);
Remove(S,E);
Exercise 01
(A) 25
(B) 20
(C) 12
(D) 45
(E) 32
Answer: D
Exercise 02.
(A) 25
(B) 20
(C) 12
(D) 45
(E) 32
Answer: C
Exercise 03.
(A) 25
(B) 20
(C) 12
(D) 45
(E) 32
Exercise 04.
(A) 25
(B) 20
(C) 12
(D) 45
(E) 32
Exercise: E
Exercise 05.
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);
(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.
(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
[Front]
Exercise 08.
Assume that Q contains the following names prior to the call to QueueMystery.
[Front]
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?
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
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?
Answer: E