Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 61

Queues

Lecture 4
Lecture Objectives
In this lecture, you will:

 Learn about queues


 Examine various queue operations
 Learn how to implement a queue as an array
 Learn how to implement a queue as a linked list
 Examine the STL class queue
 Discover queue applications
 Learn about priority queue
 Examine the STL class priority_queue
Queues
The notion of a queue in computer science is the same as the
notion of the queues to which you are accustomed in everyday life.

Examples:

1 – There are queues of customers in a bank.


2 – There are queues of customers in a grocery store.
3 – There are queues of cars waiting to pass through a
tollbooth.

4 - Similarly, because a computer can send a print request


faster than a printer can print, a queue of documents is
often waiting to be printed at a printer.
A bank line at time (a) 0; (b) 12; (c) 20; (d) 38
Queues
Definition: A queue is a set of elements of the same type in
which the elements are added at one end, called the back or
rear, and deleted from the other end, called the front or first
First In First Out (FIFO) data structure
FIFO
First In First Out (FIFO) data structure

The general rule to process elements in a


queue is that the customer at the front of the
queue is served next and that when a new
customer arrives, he or she stands at the end
of the queue. That is, a queue is a First In
First Out, or simply FIFO data structure.
ADT Queue Operations

1. Create an empty queue.


2. Destroy a queue.
3. Determine whether a queue is empty.
4. Add a new item to the queue.
5. Remove from the queue the item that was
added earliest.
6. Retrieve from the queue the item that was
added earliest.
An Array-Based Implementation
For applications in which a fixed-sized queue does not present a problem, you can use
an array to represent a queue. As figure (a) illustrates, a naive array-based
implementation of a queue might include the following definitions:

const int MAX_QUEUE = maximum-size-of-queue;


typedef desired-type-of-queue-item QueueItemType;
QueueItemType items[MAX_QUEUE];
int front;
int back;

Figure
Here front and back are the indices of (a) A naive array-based implementation of a queue;
the front and back items, respectively, (b) rightward drift can cause the queue to appear full
in the queue. Initially, front is 0 and
back is -1. To insert a new item into the The problem with this strategy is rightward
queue, you increment back and place drift – that is, after a sequence of additions
the item in items[back]. To delete an
and removals, the items in the queue will drift
item, you simply increment front.
toward the end of the array, and back could
equal MAX_QUEUE – 1 even when the
The queue is empty whenever back is greater than front. queue contains only a few items. Figure (b)
The queue is full when back equals MAX_QUEUE -1. illustrates this situation.
An Array-Based Implementation

One possible solution to this problem is to


shift array elements to the left, either after
each deletion or whenever back equals
MAX_QUEUE – 1. This solution guarantees
that the queue can always contain up to
MAX_QUEUE items. Shifting is not really
satisfactory, however, as it would dominate
the cost of the implementation.
An Array-Based Implementation

A much more elegant solution


is possible by viewing the
array as circular, as Figure
illustrates. You advance the Figure
A circular implementation of a queue
queue indexes front (to delete
an item) and back (to insert an
item) by moving them
clockwise around the array
An Array-Based Implementation
Figure illustrates the effect of a sequence of three queue operations on
front, back, and the array. When either front or back advances past
MAX_QUEUE – 1, it wraps around to 0. This wraparound eliminates the
problem of rightward drift, which occurred in the previous
implementation, because here the circular array has no end.

The only difficulty with this scheme involves detecting the queue-empty
and queue-full conditions.
An Array-Based Implementation
It seems reasonable to select as the queue-empty condition
front is one slot ahead of back
since this appears to indicate that front “passes” back when the queue becomes
empty, as Figure (a) depicts. However, it is also possible that this condition signals a
full queue: Because the queue is circular, back might in fact “catch up” with front as
the queue becomes full; Figure (b) illustrates this situation.

Figure (a) front passes back when the queue becomes Figure (b) back catches up to front when the queue
empty becomes full
An Array-Based Implementation

Obviously, you need a way to distinguish


between the two situations. One such way is
to keep a count of the number of items in the
queue.
=> Before inserting into the queue, you check
to see if the count is equal to MAX_QUEUE:
if it is, the queue is full.
=> Before deleting an item from the queue,
you check to see if the count is zero; if it is,
the queue is empty.
An Array-Based Implementation
To initialize the queue, you set front to 0, back to MAX_QUEUE – 1, and
count to 0. You obtain the wraparound effect of a circular queue by
using modulo arithmetic (that is, the C++ % operator) when
incrementing front and back.
For example,
example you can insert newItem into the queue by using the
statements
back = (back+1) % MAX_QUEUE;
items[back] = newItem;
++count;
Notice that if back equaled MAX_QUEUE -1 before the insertion of
newItem, the first statement, back = (back+1) % MAX_QUEUE, would
have the effect of wrapping back around to location 0.
Similarly, you can delete the item at the front of the queue by using the
statements
front = (front+1) % MAX_QUEUE;
--count;
An Array-Based Implementation

Sometimes you do not require a count of the


number of items in the queue. One approach uses
a flag isFull to distinguish between the full and
empty conditions. The expense of maintaining an
isFull flag is about the same as that of maintaining
a counter.
However, a faster implementation declares
MAX_QUEUE + 1 locations for the array items, but
uses only MAX_QUEUE of them for queue items.
You sacrifice one array location and make front the
index of the location before the front of the queue.
An Array-Based Implementation
As Figure illustrates, the queue is full if
front equals (back+1) % (MAX_QUEUE+1)
But the queue is empty if
front equals back
This implementation does not have the overhead
of maintaining a counter or flag, and so is more
efficient time-wise.

Figure: A more efficient circular


implementation
(a) a full queue; (b) an empty queue
An Array-Based Implementation
of
the ADT Queue
An Array-Based Implementation of the ADT Queue
class Queue
{
public:
// constructors and destructor:
Queue(); // default constructor
// copy constructor and destructor are supplied by the compiler

// Queue operations:

bool isEmpty() const; Declaration


void enqueue(QueueItemType newItem);
of class
void dequeue();
void dequeue(QueueItemType& queueFront); Queue
void getFront(QueueItemType& queueFront) const;
void display() const;

private:
QueueItemType items[MAX_QUEUE];
int front;
int back;
int count;
}; // end Queue class
An Array-Based Implementation of the ADT Queue

Queue ::Queue() : front(0), back(MAX_QUEUE-1), count(0)


{
default
} // end default constructor
constructor

bool Queue ::isEmpty() const


{
return bool(count==0);
} // end isEmpty

Function to check
whether queue is
empty or not
An Array-Based Implementation of the ADT Queue

void Queue ::enqueue(QueueItemType newItem)


{
if(count == MAX_QUEUE)
cout << "queue full on enqueue";
else Function to add
{ // queue is not full; insert item an item to the
queue
back = (back+1) % MAX_QUEUE;
items[back] = newItem;
++count;
} // end if
} // end enqueue
An Array-Based Implementation of the ADT Queue

void Queue ::dequeue()

if (isEmpty())

cout << "empty queue, cannot dequeue" ;


Function to
else remove front
item from the
{ // queue is not empty; remove front queue
front = (front+1) % MAX_QUEUE;

--count;

} // end if

} // end dequeue
An Array-Based Implementation of the ADT Queue
void Queue ::dequeue(QueueItemType& queueFront)
{
if (isEmpty())
cout << "empty queue, cannot dequeue\n" ;
else
Function to
{ // queue is not empty; retrieve and remove front retrieve and
queueFront = items[front]; remove front
front = (front+1) % MAX_QUEUE; item from the
queue
--count;
cout << "Front element of the queue is “;
cout << queueFront << endl;
} // end if
} // end dequeue
An Array-Based Implementation of the ADT Queue

void Queue ::getFront(QueueItemType& queueFront) const

if (isEmpty())

cout << "empty queue, cannot getFront" ;

else Function to
{ // queue is not empty; retrieve front retrieve front
item of the
queueFront = items[front]; queue
cout << "Front element of the queue is “;

cout << queueFront << endl;

} // end getFront
An Array-Based Implementation of the ADT Queue

void Queue ::display() const

cout << "Items in the queue : [";

i = front - 1;
Function to
do display all the
items of the
{ i = (i+1)% MAX_QUEUE; queue

cout << setw(3) << items[i];

} while (i != back)

cout << " ]\n";

} // end display
An Array-Based Implementation of the ADT Queue
#include <iostream>
#include <iomanip> MAX_QUEUE = 5, means size of the queue is 5.
const int MAX_QUEUE = 5;
typedef int QueueItemType;
using namespace std;
int main()
{
Sample Run
int a;
Queue aQueue;
aQueue.enqueue(15); Output
aQueue.enqueue(20);
aQueue.enqueue(25); Items in the queue : [ 15 20 25 33 ]
aQueue.enqueue(33);
aQueue.display();
Front element of the queue is 15
aQueue.getFront(a); Items in the queue : [ 25 33 ]
aQueue.dequeue();
aQueue.dequeue(); Front element of the queue is 25
aQueue.display(); Items in the queue : [ 33 ]
aQueue.dequeue(a);
aQueue.display(); Press any key to continue . . .
system ("pause");
return 0;
}
A Pointer-Based Implementation
A pointer-based implementation of a queue could use a linear linked
list with two external pointers, one to the front and one to the back,
as Figure (a) illustrates.
Figure (b) shows that you can actually get by with a single external
pointer – to the back – if you make the linked list circular.

Figure: A pointer-based implementation of a queue


(a) a linear linked list with two external pointers
(b) a circular linear linked list with one external pointer
A Pointer-Based Implementation
Insertion at the back and deletion from the front are straightforward.
Figure illustrates the addition of an item to a nonempty queue.
Inserting the new node, to which newPtr points, at the back of the
queue requires three pointer changes: the next pointer in the new
node, the next pointer in the current back node, and the external
pointer backPtr.
Figure depicts these changes and indicates the order in which they
can occur. (The dashed lines indicate pointer values before the
changes.)

Figure: Inserting an item into a nonempty queue


A Pointer-Based Implementation
The addition of an item to an empty queue is a special case, as Figure
illustrates.
frontPtr = newPtr;
backPtr = newPtr;
(a) (b)

3 3
frontPtr frontPtr

backPtr newPtr backPtr newPtr

Figure: Inserting an item into an empty queue


(a) before insertion
(b) after insertion
A Pointer-Based Implementation

Deletion from the front of the queue is simpler than insertion


at the back. Figure illustrates the removal of the front item of
a queue that contains more than one item. Notice that you
need to change only the external pointer frontPtr.
Deletion from a queue of one item is a special case that sets
the external pointers backPtr and frontPtr to NULL.

Figure: Deleting an item from a queue of more than one item


A Pointer-Based Implementation
of
the ADT Queue

(linked queue)
A Pointer-Based Implementation of the ADT Queue
class Queue
{
public:
// constructors and destructor:
Queue(); // default constructor
Queue(const Queue& Q); // copy constructor
~Queue(); // destructor
// Queue operations:
bool isEmpty() const;
// Determines whether the queue is empty.
// Precondition: None.
// Postcondition: Returns true if the queue is empty, otherwise returns false.
void enqueue(QueueItemType newItem);
// Inserts an item at the back of a queue.
// Precondition: newItem is the item to be inserted.
// Postcondition: If the insertion is successful, newItem is at the back of the queue.
A Pointer-Based Implementation of the ADT Queue
void dequeue();
// Dequeues the front of a queue.
// Precondition: None.
// Postcondition: If the queue is not empty, the item
// that was added to the queue earliest is deleted.
void dequeue(QueueItemType& queueFront);
// Retrieves and deletes the front of a queue.
// Precondition: None.
// Postcondition: If the queue is not empty, queueFront
// contains the item that was added to the queue earliest, and the item is deleted.
void getFront(QueueItemType& queueFront) const;
// Retrieves the item at the front of a queue.
// Precondition: None.
// Postcondition: If the queue is not empty, queueFront
// contains the item that was added to the queue earliest.
void display();
// Displays the elements which are in the queue.
A Pointer-Based Implementation of the ADT Queue

private:
// The queue is implemented as a linked list with one external pointer to the front
// of the queue and a second external pointer to the back of the queue.
struct QueueNode
{ QueueItemType item;
QueueNode *next;
}; // end struct
QueueNode *frontPtr;
QueueNode *backPtr;
QueueNode *curPtr;
}; // end Queue class
A Pointer-Based Implementation of the ADT Queue

Queue ::Queue() : backPtr(NULL), frontPtr(NULL)


default constructor {
} // end default constructor

Queue ::Queue(const Queue& Q)


copy constructor { // Implementation left as an exercise.
} // end copy constructor

Queue ::~Queue()
{
while (!isEmpty())
destructor
dequeue();
// Assertion: frontPtr and backPtr equal NULL
} // end destructor
A Pointer-Based Implementation of the ADT Queue

bool Queue::isEmpty() const


{
return bool(backPtr == NULL);
} // end isEmpty

Function to check whether queue is empty or not.


A Pointer-Based Implementation of the ADT Queue
void Queue::enqueue(QueueItemType newItem)
{
if (isEmpty())
{
frontPtr = new QueueNode;
frontPtr ->item = newItem;
frontPtr ->next = NULL;
backPtr = frontPtr;
} Function to add
else an item to the
{ // create a new node queue
QueueNode* newPtr = new QueueNode;
newPtr ->item = newItem;
newPtr ->next = NULL;
backPtr ->next = newPtr;
backPtr = newPtr;
}
} // end enqueue
A Pointer-Based Implementation of the ADT Queue

void Queue ::dequeue()


{
if (isEmpty())
cout << "empty queue, cannot dequeue" ;
else
{ // queue is not empty; remove front
QueueNode *tempPtr = frontPtr;
if (frontPtr == backPtr) // special case? Function to
{ // yes, one node in queue remove front
frontPtr = NULL; item from
backPtr = NULL; the queue
}
else
frontPtr = frontPtr->next;
tempPtr ->next = NULL; // defensive strategy
delete tempPtr;
} // end if
} // end dequeue
A Pointer-Based Implementation of the ADT Queue

void Queue ::dequeue(QueueItemType& queueFront)


{
if (isEmpty())
cout << "empty queue, cannot dequeue\n" ;
else
{ // queue is not empty; retrieve and remove front
queueFront = frontPtr ->item;
dequeue(); // delete front
cout << "Front element of the queue is “ << queueFront << endl;
} // end if
} // end dequeue

Function to retrieve and remove front item from the queue


A Pointer-Based Implementation of the ADT Queue
void Queue ::getFront(QueueItemType& queueFront) const
{
if (isEmpty())
cout << "empty queue, cannot getFront" ;
else
{ // queue is not empty; retrieve front
queueFront = frontPtr ->item;
cout << "Front element of the queue is " << queueFront << endl;
}
} // end getFront

Function to retrieve front item of the queue


A Pointer-Based Implementation of the ADT Queue

void Queue ::display()


{
curPtr = frontPtr;
cout << "Items in the queue : [";
while (curPtr != NULL) Function to
{ display all the
items of the
cout << setw(3) << curPtr ->item;
queue
curPtr = curPtr ->next;
}
cout << " ]\n";
} // end display
A Pointer-Based Implementation of the ADT Queue
#include <iostream>
#include <iomanip> QueueItemType is int type, that is, the
typedef int QueueItemType; queue can contain only integer numbers.
using namespace std;
int main()
{ Sample Run
int x;
Queue aQueue;
aQueue.enqueue(3);
aQueue.enqueue(5);
Output
aQueue.enqueue(7); Items in the queue : [ 3 5 7 9 ]
aQueue.enqueue(9);
aQueue.display();
Front element of the queue is 3
aQueue.getFront(x); Items in the queue : [ 7 9 ]
aQueue.dequeue();
Front element of the queue is 7
aQueue.dequeue();
aQueue.display(); Items in the queue : [ 9 ]
aQueue.dequeue(x);
Press any key to continue . . .
aQueue.display();
system ("pause");
return 0;
}
The Standard Template Library Class queue
THE queue INTERFACE
template <class T>
class queue
{
public:
queue(); // default constructor
queue(const queue&); // copy constructor
~queue(); // destructor
queue& operator=(const queue&); // assignment operator
int size() const; // returns number of elements
bool empty() const; // returns true iff is empty
T& front(); // returns the element in front
T& back(); // returns the element in back
void push(const T&); // inserts given element at back
void pop(); // removes element from front
};
Using a queue of Strings
#include<iostream>
#include<queue>
Sample Run
using namespace std;
int main()
{ queue<string> q;
Output
q.push ("Manoj"); Size of queue is : 5
q.push ("Sharaf");
q.push ("Wong");
q.push ("Azyyati"); Front element of the queue is Manoj
q.push ("Norhana"); Back element of the queue is Norhana
cout << "Size of queue is : " << q.size() << endl;
cout << "\nFront element of the queue is ";
cout << q.front(); Elements in the queue :
cout << "\nBack element of the queue is ";
cout << q.back(); Manoj
cout << "\n\nElements in the queue :\n\n"; Sharaf
while (!q.empty()) Wong
{
Azyyati
cout << q.front() << endl;
q.pop(); Norhana
}
cout << endl; Press any key to continue . . .
system("pause");
return 0;
}
Simple Applications
Of
The ADT Queue
Reading a String of Characters
When you enter characters at a keyboard, the system must
retain them in the order in which you typed them. It could use a
queue for this purpose, as the following pseudocode indicates:

// read a string of characters from a


// single line of input into a queue
aQueue.createQueue()
While (not end of line)
{ Read a new character ch
aQueue.enqueue(ch)
} // end while
Once the characters are in a queue, the system can
process them as necessary.
For example:
If you had typed an integer – without any mistakes,
but possibly preceded or followed by blanks – the
queue would contain digits and possibly blanks.

If the digits are 2, 4, and 7, the system could


convert them into the decimal value 247 by
computing

10 * (10 * 2 + 4) + 7
The following pseudocode performs this conversion in general:

// convert digits in a queue aQueue into a


// decimal integer n

// get first digit, ignoring any leading blanks


do
{ aQueue.dequeue(ch)
} while (ch is blank)
// Assertion: ch contains first digit
// compute n from digits in queue
n=0
done = false
do
{ n = 10 * n + integer that ch represents
if (!aQueue.isEmpty())
aQueue.dequeue(ch)
else
done = true
} while (!done and ch is a digit)
// Assertion: n is result
Recognizing Palindromes
As you know that:
a palindrome is a string of characters that
reads the same from left to right as it does
from right to left.

As you have learned that you can use a stack to


reverse the order of occurrences. You should realize by
now that you can use a queue to preserve the order of
occurrences.

Thus, you can use both a queue and a stack to


determine whether a string is a palindrome.
As you traverse the character string from left to right, you can insert each
character into both a queue and a stack. Figure illustrates the result of this
action for the string abcbd, which is not a palindrome.

You can see that the first character in the string is at the front of the queue and
the last character in the string is at the top of the stack.

Thus, characters removed from the queue will occur in the order in which they
appear in the string; characters removed from the stack will occur in the
opposite order.

Figure: The results of inserting a string into both a queue and a stack
Knowing this, you can compare the characters at the
front of the queue and the top of the stack. If the
characters are the same, you can delete them. You
repeat this process until either the ADTs become
empty, in which case the original string is a
palindrome, or the two characters are not the same, in
which case the string is not a palindrome.

Figure: The results of inserting a string into both a queue and a stack
The following is a pseudocode version of a nonrecursive
recognition algorithm for the language of palindromes:

isPal(in str:string) : boolean // compare the queue characters

// Determines whether str is a palindrome. // with the stack characters

// create an empty queue and an empty stack While (aQueue is not empty and
charactersAreEqual)
aQueue.createQueue()
{ aQueue.getFront(queueFront)
aStack.createStack()
aStack.getTop(stackTop)
// insert each character of the string into both
// the queue and the stack if (queueFront equals stackTop)
length = length of str { aQueue.dequeue()

for (i = 1 through length) aStack.pop()


}
{ nextChar = ith character of str
else
aQueue.enqueue(nextChar)
charactersAreEqual = false
aStack.push(nextChar) } // end while
} // end for
return charactersAreEqual
PRIORITY QUEUES
PRIORITY QUEUES

The use of a queue structure ensures that the items


are processed in the order they are received.

For example,
example in a banking environment, the customers
who arrive first are served first.

However, there are certain situations when this First


In First Out rule needs to be relaxed somewhat.

In a hospital environment, patients are, usually, seen


in the order they arrive. Therefore, you could use a
queue to ensure that the patients are seen in the order
they arrive.
PRIORITY QUEUES

However, if a patient arrives with severe or life


threatening symptoms, they are treated first. In other
words, these patients take priority over the patients
who can wait to be seen, such as those awaiting their
routine annual check-up.

For another example, in a shared environment, when


print requests are sent to the printer, interactive
programs take priority over batch processing
programs
PRIORITY QUEUES
There are many other situations where some priority is
assigned to the customers.

To implement such a data structure in a program, we use


special types of queues, called priority queues.
queues

In a priority queue, customers or jobs with higher priority


are pushed to the front of the queue.

One way to implement a priority queue is to use an


ordinary linked list, which keeps the items in order from
the highest to lowest priority.

However, an effective way to implement a priority queue


is to use a treelike structure.
STL class priority_queue
Here is part of the interface for the Standard C++ priority_queue
container class template:
template <class T> These are the same
class priority_queue functions as for the queue
{ class template. The only
public:
significant difference is that
priority_queue();
the priority_queue class
priority_queue(const priority_queue&);
requires the template
~priority_queue();
parameter T to be an
priority_queue& operator=(const priority_queue&);
ordinal type (i.e., a type for
int size() const; // returns number of elements
bool empty() const; // returns true iff this is empty
which the comparison
const T& top() const; // returns the front element
operators are defined) so
void push(const T&); // inserts given element at back that the pop() and top()
void pop(); // removes element from front functions remove and
private: return the highest priority
// . . . . element instead of the first
}; element.
USING priority_queue OBJECTS
EXAMPLE: Testing a Priority Queue of void print(PriorityQueue q)
{ cout << "size=" << q.size();
Integers if (q.size()>0)
#include< iostream> { cout << " , top=" << q.top()
#include<queue> // defines the priority_queue class template cout << ": (" << q.top();
q.pop();
using namespace std; while (!q.empty())
{ cout << "," << q.top();
typedef priority_queue<int> PriorityQueue; q.pop();
}
void print(PriorityQueue); // prints a copy of given queue cout << ")";
}
int main()
cout << "\n";
{ PriorityQueue pq; print(pq);
}
pq.push(44); print(pq);
pq.push(66); print(pq);
pq.push(22); print(pq);
pq.push(55); print(pq); Output
pq.push(33); print(pq); size=0
system ("pause");
size=1, top=44: (44)
}
size=2, top=66: (66, 44)
size=3, top=66: (66, 44, 22)
size=4, top=66: (66, 55, 44, 22)
size=5, top=66: (66, 55, 44, 33, 22)
PRIORITY QUEUES

The print() function suggests that the elements are stored in


the priority queue in descending order

But that is probably not true. The function uses the


priority_queue ::pop() function to access the elements, and
that member function is required to remove the elements in
order of their priority.

But there is no obligation on the priority_queue itself to


store the elements in any particular order.

Indeed, most implementations keep the elements only


partially ordered.

You might also like