CSC-221 DATA STRUCTURES AND ALGORITHMS
Instructor: Ayesha Jamal
Lecture : Queue & its Static & Dynamic implementation
QUEUE
2
QUEUE
A queue is an ordered collection of homogenous data items such that:
Items can be removed only at one end (front of the queue)
Items can be added only at the other end (rear of the queue)
Accessing the elements of queues follows a First In, First Out (FIFO) order
Example: Customers standing in a check-out line in a store model a queue, the first customer in is
the first customer served.
4
QUEUE AS AN ADT
A Queue is a collection of items on which following operations are defined:
Initialize the queue to be the empty queue.
Determine whether or not the queue is empty.
Determine whether or not the queue is full.
If Queue is not full, insert a new item from the rear of the queue.
If Queue is not empty, remove an item from the front of the queue.
Retrieve the value at the front of the queue
ENQUEUE AND DEQUEUE
Primary queue operations: Enqueue and Dequeue
Enqueue – insert an element at the rear of the queue.
Dequeue – remove an element from the front of the queue.
APPLICATIONS OF QUEUES
Operating system
Multi-user/multitasking environments, where several users or task may be requesting the same resource
simultaneously
Printer Queue
Documents to be printed in a queue
LANDING AND TAKEOFF QUEUES
QUEUES VS. STACKS
Stacks are a LIFO container: Store data in the reverse of order received
Queues are a FIFO container: Store data in the order received
Stacks then suggest applications where some sort of reversal or unwinding is desired.
Queues suggest applications where service is to be rendered relative to order received.
Stacks and Queues can be used in conjunction to compare different orderings of the
same data set.
From an ordering perspective, then, Queues are the “opposite” of stacks.
QUEUES VS. STACK
QUEUE OPERATIONS
Mainly the following four basic operations are performed on queue:
enqueue(): Adds an item to the queue. If the queue is full, then it is said
to be an Overflow condition.
dequeue(): Removes an item from the queue. If the queue is empty, then
it is said to be an Underflow condition.
is Full(): Check if queue is full.
is Empty(): Check if queue is empty.
11
ENQUEUE OPERATION
Queues maintain two data variables, front and rear. Therefore, its operations
are comparatively difficult to implement than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue −
Step 1 − Check if the queue is full.
Step 2 − If the queue is full, produce overflow error and exit.
Step 3 − If the queue is not full, increment rear to point the next empty space.
Step 4 − Add data element to the queue location, where the rear is pointing.
Step 5 − return success.
12
13
DEQUEUE OPERATION
Accessing data from the queue is a process of two tasks − access the data
where front is pointing and remove the data after access.
The following steps are taken to perform dequeue operation −
Step 1 − Check if the queue is empty.
Step 2 − If the queue is empty, produce underflow error and exit.
Step 3 − If the queue is not empty, access the data where front is pointing and
remove it.
Step 4 − Increment front to point to the next available data element.
14
Step 5 − Return success.
15
IMPLEMENTATION OF QUEUES
Static
Queue is implemented by an array, and size of queue remains fix.
Dynamic
A queue can be implemented as a linked list, and expand or shrink with each enqueue or dequeue
operation.
IMPLEMENTATION OF QUEUE
There are two ways to implement a queue:
Using array
Using linked list
17
STATIC IMPLEMENTATION
An implementation of a queue requires:
Storage for the data
Markers for the front and rear of the queue
An array-based implementation would need structures like:
myArray, an array to store the elements of the queue
front, an index to track the front queue element
rear, an index to track the rear queue element
Queue Implementation Using
Array
IMPLEMENTATION OF QUEUES
class Queue
{
private: /***** Data Members *****/
int myArray;
int size;
int front, rear;
public: /***** Function Members *****/
Queue(int);
bool isEmpty();
bool isFull();
void enqueue(int value);
int getFront();
int dequeue();
};
IMPLEMENTATION OF QUEUES
IMPLEMENTATION OF QUEUES
Queue::Queue(int s)
{
myArray = new int[s];
size = s;
front = rear = -1;
}
IMPLEMENTATION OF QUEUES
IsEmpty & IsFull OPERATION
bool Queue::isEmpty() bool Queue::isFull()
{ {
if (front == -1)&&(rear == -1) if (rear == size - 1)
return true; return true;
else else
return false; return false;
} }
IMPLEMENTATION OF QUEUES
ENQUEUE OPERATION
void Queue::enqueue(int value)
{
if (isFull()) //Queue is Full
cout<<“Queue is full”<<endl;
else if (isEmpty()) //Queue is Empty
{
front=rear=0;
}
else
{
rear++; //there is some element in queue
}
myArray[rear] = value;
}
DEQUEUE OPERATION
int Queue::dequeue() else
{ {
int data;
data=myArray[front];
if (isEmpty()) //Queue is Empty //there is some element in queue
{ front++;
cout<<“Queue is empty”<<endl;
}
}
else if(front==rear) return data;
{ }
data=myArray[front];
front=rear=-1;
}
IMPLEMENTATION OF QUEUES
int Queue::getFront()
{
if (isEmpty())
{
cout<<“Queue is empty”<<endl;
}
else
return myArray[front];
}
QUEUE USING ARRAY
Pros: Easy to implement.
Cons : Fixed size, if the queue has a large number of enqueue and
dequeue operations, at some point (in case of linear increment of front
and rear indexes) we may not be able to insert elements in the queue even
if the queue is empty (this problem is avoided by using circular queue).
26
Queue Implementation Using
Linked List
LINKED QUEUES
Queues can be implemented using linked lists
Enqueue operation is same as adding an element at the end of the list
Dequeue operation is same as removing the first element from the list
28
ENQUEUE
29
DEQUEUE
30
LINKED QUEUE
class LinkedQueue
{
private:
Node * front;
Node * rear;
public:
LinkedQueue();
void Enqueue(int);
int Dequeue();
int Front();
void Display(); 31
};
LINKED QUEUE
LinkedQueue::LinkedQueue(){
front = NULL;
rear = NULL;
}
void LinkedQueue::Display() {
Node* temp = front;
while(temp != NULL)
{
cout<<temp->data<<endl;
temp = temp->next;
}
32
}
ENQUEUE – ILLUSTRATION
front rear
Node* temp = new Node;
temp->data =x;
temp->next = NULL;
rear->next = temp;
rear = temp; rear temp
ENQUEUE OPERATION
void LinkedQueue:: Enqueue(int x) {
Node* temp = new Node;
temp->data =x;
temp->next = NULL;
if(front == NULL && rear == NULL) //List is empty
{
front = rear = temp;
}
else
rear->next = temp; //List is not empty 34
rear = temp;}
DEQUEUE – ILLUSTRATION
front temp front rear
temp = front;
front = front->next;
delete temp;
front rear 35
DEQUEUE – ILLUSTRATION (LIST HAS ONE NODE ONLY)
temp
front rear front rear
temp = front;
front = rear = NULL;
delete temp;
36
DEQUEUE OPERATION
void LinkedQueue:: Dequeue()
{
Node* temp = front;
if(front == NULL)
{ cout<<"Queue is Empty\n"; return; }
else if(front == rear)
{ front = rear = NULL; }
else
{front = front->next; }
delete temp; 37
}
LINKED QUEUE FRONT OPERATION
int LinkedQueue::Front()
{
if(front == NULL)
{
cout<<"Queue is empty\n“;
return -1;
}
else
return front->data;
}
38
REAL LIFE APPLICATIONS OF QUEUE
1) When a resource is shared among multiple consumers. Examples include CPU
scheduling, Disk Scheduling.
2) When data is transferred asynchronously (data not necessarily received at same rate as
sent) between two processes. Examples include IO Buffers, pipes, file IO, etc.
3) In Operating systems:
a) Semaphores
b) FCFS ( first come first serve) scheduling, example: FIFO queue
c) Spooling in printers
d) Buffer for devices like keyboard
4) In Networks:
a) Queues in routers/ switches
b) Mail Queues 39
Circular Queue
CIRCULAR QUEUE
Solution: Shifting of elements?
Shifting – Inefficient esp. when elements are large records
Would prefer to let the data “wrap-around” to the beginning
Use a circular array to implement the queue
In this implementation, first position follows the last
0 1 2 3 4 5 6 7
myArray 44 55 11 22 33
CIRCULAR QUEUE
0 1 2 3 4
front= 3
myArray 15 rear = 4
front rear
Allow rear to wrap around the array.
if(rear == size-1)
rear = 0;
else
rear++;
Equivalently:
rear = (rear + 1) % size;
IMPORTANT
Initialize both front and rear to 0.
front
rear 0 7 Queue::Queue()
{
front = rear= 0;
1 6 }
2 5
3 4
CIRCULAR QUEUE
enqueue(70); front
front
rear 0 7
0 7
70
rear
1 6 1 6
2 5 2 5
3 4 3 4
CIRCULAR QUEUE
front
0 7
70
enqueue(70);
enqueue(80);
1 80 6
rear 5
2
3 4
CIRCULAR QUEUE
front
0 7
enqueue(70); 70
enqueue(80);
enqueue(90); 1 80 6
90
2 5
3 4
rear
CIRCULAR QUEUE
void Queue::enqueue(int value)
{
if (! isFull())
{
myArray[rear] = value;
rear = (rear + 1) % size;
}
else
cout<<“Queue Overflow”;
}
CIRCULAR QUEUE
front 0 7
0 7 front
70
70
1 80 6
1 80 6
dequeue();
90
5
90 2
2 5
3 4
3 4
rear rear
CIRCULAR QUEUE
0 7
dequeue();
dequeue(); 1 6
int Queue::dequeue()
{ front 90
5
if (! isEmpty()) { 2
int val= myArray[front] ;
front = (front + 1) % size; 3 4
return val;}
else
cout<<“Queue Underflow”; rear
return -1;
CIRCULAR QUEUE
rear
0 7
90
enqueue(50); 1 80 6
enqueue(60);
enqueue(70);
enqueue(80); 70
front 90
enqueue(90); 2 5
50 60
3 4
EMPTY VS. FULL QUEUE
rear
front
enqueue(30); 0
rear 0 7 enqueue(40);
7
90
1 80 6
1 6
90 70
front 5
2
2 5
50 60
3 4
3 4
EMPTY VS. FULL QUEUE
front
0
rear 0 7
7
30 90
1 80 6
1 6 40
rear
90 70
2 5
2 5 front
50 60
3 4
3 4
front == rear; //Empty or full?
HOW TO DISTINGUISH B/W EMPTY AND FULL QUEUES
Can be tricky
Use a counter variable to keep track of number of elements in the queue
class Queue
{
private:
int *myArray;
int front, rear, size;
int count;
};
class Queue
{
/***** Function Members *****/
public:
Queue(int);
bool isEmpty();
bool isFull();
void enqueue(int value);
int getFront();
int dequeue();
/***** Data Members *****/
private:
int *myArray;
int front, rear, count, size;
};
ISFULL & ISEMPTY
Queue::Queue(int s)
{
myArray=new int[s];
size = s;
front = rear = count =0;
}
bool Queue::isEmpty() bool Queue::isFull()
{ {
if (count == 0) if (count == size)
return true;
return true;
else
return false;
else
} return false;
}
DISPLAY FRONT
int Queue::getFront()
{
if (!isEmpty())
{
return myArray[front];
}
else
return -1;
}
ENQUEUE OPERATION
void Queue::enqueue(int value)
{
if (! isFull()){
myArray[rear] = value;
rear = (rear + 1) % size;
count++;
}
}
DEQUEUE OPERATION
int Queue::dequeue()
{
if (! isEmpty()) {
int val= myArray[front]
;
front = (front + 1) %
size;
count--;
return val;}
else
cout<<“Queue Underflow”;
58
return -1;
}
TRAVERSE QUEUE
void Queue::display()
{
int f=front, r=rear;
if (! isEmpty()) {
while(f!=r)
{
cout<<myArray[f];
front = (front + 1) % size;
}
else
59
cout<<“Queue is empty”;
}