Circular Queue
Why was the concept of the circular queue introduced?
There was one limitation in the array implementation of Queue. If the rear reaches to the end
position of the Queue then there might be possibility that some vacant spaces are left in the
beginning which cannot be utilized. So, to overcome such limitations, the concept of the
circular queue was introduced.
As we can see in the above image, the rear is at the last position of the Queue and front is
pointing somewhere rather than the 0 th position. In the above array, there are only two
elements and other three positions are empty. The rear is at the last position of the Queue; if
we try to insert the element then it will show that there are no empty spaces in the Queue.
There is one solution to avoid such wastage of memory space by shifting both the elements at
the left and adjust the front and rear end accordingly. It is not a practically good approach
because shifting all the elements will consume lots of time. The efficient approach to avoid
the wastage of the memory is to use the circular queue data structure.
What is a Circular Queue?
A circular queue is similar to a linear queue as it is also based on the FIFO (First In First Out)
principle except that the last position is connected to the first position in a circular queue that
forms a circle. It is also known as a Ring Buffer.
Operations on Circular Queue
The following are the operations that can be performed on a circular
queue:
o Front: It is used to get the front element from the Queue.
o Rear: It is used to get the rear element from the Queue.
o enQueue(value): This function is used to insert the new value in the
Queue. The new element is always inserted from the rear end.
o deQueue(): This function deletes an element from the Queue. The
deletion in a Queue always takes place from the front end.
Applications of Circular Queue
The circular Queue can be used in the following scenarios:
o Memory management: The circular queue provides memory
management. As we have already seen that in linear queue, the memory
is not managed very efficiently. But in case of a circular queue, the
memory is managed efficiently by placing the elements in a location which
is unused.
o CPU Scheduling: The operating system also uses the circular queue to
insert the processes and then execute them.
o Traffic system: In a computer-control traffic system, traffic light is one of
the best examples of the circular queue. Each light of traffic light gets ON
one by one after every jinterval of time. Like red light gets ON for one
minute then yellow light for one minute and then green light. After green
light, the red light gets ON.
Enqueue operation
The steps of enqueue operation are given below:
o First, we will check whether the Queue is full or not.
o Initially the front and rear are set to -1. When we insert the first element in
a Queue, front and rear both are set to 0.
o When we insert a new element, the rear gets incremented,
i.e., rear=rear+1.
Scenarios for inserting an element
There are two scenarios in which queue is not full:
o If rear != max - 1, then rear will be incremented to mod(maxsize) and
the new value will be inserted at the rear end of the queue.
o If front != 0 and rear = max - 1, it means that queue is not full, then
set the value of rear to 0 and insert the new element there.
There are two cases in which the element cannot be inserted:
o When front ==0 && rear = max-1, which means that front is at the first position of
the Queue and rear is at the last position of the Queue.
o front== rear + 1;
Algorithm to insert an element in a circular queue
Step 1: IF (REAR+1)%MAX = FRONT
Write " OVERFLOW "
Goto step 4
[End OF IF]
Step 2: IF FRONT = -1 and REAR = -1
SET FRONT = REAR = 0
ELSE IF REAR = MAX - 1 and FRONT ! = 0
SET REAR = 0
ELSE
SET REAR = (REAR + 1) % MAX
[END OF IF]
Step 3: SET QUEUE[REAR] = VAL
Step 4: EXIT
Let's understand the enqueue and dequeue operation through the
diagrammatic representation.
#include <stdio.h>
# define max 6
int queue[max]; // array declaration
int front=-1;
int rear=-1;
// function to insert an element in a circular queue
void enqueue(int element)
{
if(front==-1 && rear==-1) // condition to check queue is empty
{
front=0;
rear=0;
queue[rear]=element;
}
else if((rear+1)%max==front) // condition to check queue is full
{
printf("Queue is overflow..");
}
else
{
rear=(rear+1)%max; // rear is incremented
queue[rear]=element; // assigning a value to the queue at the rear position.
}
}
// function to delete the element from the queue
int dequeue()
{
if((front==-1) && (rear==-1)) // condition to check queue is empty
{
printf("\nQueue is underflow..");
}
else if(front==rear)
{
printf("\nThe dequeued element is %d", queue[front]);
front=-1;
rear=-1;
}
else
{
printf("\nThe dequeued element is %d", queue[front]);
front=(front+1)%max;
}
}
// function to display the elements of a queue
void display()
{
int i=front;
if(front==-1 && rear==-1)
{
printf("\n Queue is empty..");
}
else
{
printf("\nElements in a Queue are :");
while(i<=rear)
{
printf("%d,", queue[i]);
i=(i+1)%max;
}
}
}
void main()
{
int choice=1,x; // variables declaration
while(choice<4 && choice!=0) // while loop
{
printf("\n Press 1: Insert an element");
printf("\nPress 2: Delete an element");
printf("\nPress 3: Display the element");
printf("\nEnter your choice");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("Enter the element which is to be inserted");
scanf("%d", &x);
enqueue(x);
break;
case 2:
dequeue();
break;
case 3:
display();
}}
}
Deque (or double-ended queue)
In this article, we will discuss the double-ended queue or deque. We should first see a brief
description of the queue.
What is a queue?
A queue is a data structure in which whatever comes first will go out first, and it follows the
FIFO (First-In-First-Out) policy. Insertion in the queue is done from one end known as
the rear end or the tail, whereas the deletion is done from another end known as
the front end or the head of the queue.
The real-world example of a queue is the ticket queue outside a cinema hall, where the person
who enters first in the queue gets the ticket first, and the person enters last in the queue gets
the ticket at last.
What is a Deque (or double-ended queue)
The deque stands for Double Ended Queue. Deque is a linear data structure where the
insertion and deletion operations are performed from both ends. We can say that deque is a
generalized version of the queue.
Though the insertion and deletion in a deque can be performed on both
ends, it does not follow the FIFO rule. The representation of a deque is
given as follows -
Types of deque
There are two types of deque –
o Input restricted queue
o Output restricted queue
Input restricted Queue
In input restricted queue, insertion operation can be performed at only
one end, while deletion can be performed from both ends.
Output restricted Queue
In output restricted queue, deletion operation can be performed at only
one end, while insertion can be performed from both ends.
o Input restricted queue
o Output restricted queue
Input restricted Queue
In input restricted queue, insertion operation can be performed at only
one end, while deletion can be performed from both ends.
Output restricted Queue
In output restricted queue, deletion operation can be performed at only
one end, while insertion can be performed from both ends.
Operations performed on deque
There are the following operations that can be applied on a deque -
o Insertion at front
o Insertion at rear
o Deletion at front
o Deletion at rear
We can also perform peek operations in the deque along with the
operations listed above. Through peek operation, we can get the deque's
front and rear elements of the deque. So, in addition to the above
operations, following operations are also supported in deque -
o Get the front item from the deque
o Get the rear item from the deque
o Check whether the deque is full or not
o Checks whether the deque is empty or not
Now, let's understand the operation performed on deque using an
example.
Insertion at the front end
In this operation, the element is inserted from the front end of the queue.
Before implementing the operation, we first have to check whether the
queue is full or not. If the queue is not full, then the element can be
inserted from the front end by using the below conditions -
o If the queue is empty, both rear and front are initialized with 0. Now, both
will point to the first element.
o Otherwise, check the position of the front if the front is less than 1 (front <
1), then reinitialize it by front = n - 1, i.e., the last index of the array.
Insertion at the rear end
In this operation, the element is inserted from the rear end of the queue.
Before implementing the operation, we first have to check again whether
the queue is full or not. If the queue is not full, then the element can be
inserted from the rear end by using the below conditions -
ADVERTISEMENT
o If the queue is empty, both rear and front are initialized with 0. Now, both
will point to the first element.
o Otherwise, increment the rear by 1. If the rear is at last index (or size - 1),
then instead of increasing it by 1, we have to make it equal to 0.
Deletion at the front end
In this operation, the element is deleted from the front end of the queue.
Before implementing the operation, we first have to check whether the
queue is empty or not.
If the queue is empty, i.e., front = -1, it is the underflow condition, and we
cannot perform the deletion. If the queue is not full, then the element can
be inserted from the front end by using the below conditions -
If the deque has only one element, set rear = -1 and front = -1.
Else if front is at end (that means front = size - 1), set front = 0.
Else increment the front by 1, (i.e., front = front + 1).
Deletion at the rear end
In this operation, the element is deleted from the rear end of the queue.
Before implementing the operation, we first have to check whether the
queue is empty or not.
If the queue is empty, i.e., front = -1, it is the underflow condition, and we
cannot perform the deletion.
If the deque has only one element, set rear = -1 and front = -1.
If rear = 0 (rear is at front), then set rear = n - 1.
Else, decrement the rear by 1 (or, rear = rear -1).
Check empty
This operation is performed to check whether the deque is empty or not. If
front = -1, it means that the deque is empty.
Check full
This operation is performed to check whether the deque is full or not. If
front = rear + 1, or front = 0 and rear = n - 1 it means that the deque is
full.
The time complexity of all of the above operations of the deque is O(1),
i.e., constant.
Applications of deque
o Deque can be used as both stack and queue, as it supports both
operations.
o Deque can be used as a palindrome checker means that if we read the
string from both ends, the string would be the same.
Implementation of deque
#include <stdio.h>
#define size 5
int deque[size];
int f = -1, r = -1;
// insert_front function will insert the value from the front
void insert_front(int x)
{
if((f==0 && r==size-1) || (f==r+1))
{
printf("Overflow");
}
else if((f==-1) && (r==-1))
{
f=r=0;
deque[f]=x;
}
else if(f==0)
{
f=size-1;
deque[f]=x;
}
else
{
f=f-1;
deque[f]=x;
}
}
// insert_rear function will insert the value from the rear
void insert_rear(int x)
{
if((f==0 && r==size-1) || (f==r+1))
{
printf("Overflow");
}
else if((f==-1) && (r==-1))
{
r=0;
deque[r]=x;
}
else if(r==size-1)
{
r=0;
deque[r]=x;
}
else
{
r++;
deque[r]=x;
}
// display function prints all the value of deque.
void display()
{
int i=f;
printf("\nElements in a deque are: ");
while(i!=r)
{
printf("%d ",deque[i]);
i=(i+1)%size;
}
printf("%d",deque[r]);
}
// getfront function retrieves the first value of the deque.
void getfront()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
else
{
printf("\nThe value of the element at front is: %d", deque[f]);
}
// getrear function retrieves the last value of the deque.
void getrear()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
else
{
printf("\nThe value of the element at rear is %d", deque[r]);
}
// delete_front() function deletes the element from the front
void delete_front()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
else if(f==r)
{
printf("\nThe deleted element is %d", deque[f]);
f=-1;
r=-1;
}
else if(f==(size-1))
{
printf("\nThe deleted element is %d", deque[f]);
f=0;
}
else
{
printf("\nThe deleted element is %d", deque[f]);
f=f+1;
}
}
// delete_rear() function deletes the element from the rear
void delete_rear()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
else if(f==r)
{
printf("\nThe deleted element is %d", deque[r]);
f=-1;
r=-1;
}
else if(r==0)
{
printf("\nThe deleted element is %d", deque[r]);
r=size-1;
}
else
{
printf("\nThe deleted element is %d", deque[r]);
r=r-1;
}
}
void main()
{
insert_front(20);
insert_front(10);
insert_rear(30);
insert_rear(50);
insert_rear(80);
display(); // Calling the display function to retrieve the values of deque
getfront(); // Retrieve the value at front-end
getrear(); // Retrieve the value at rear-end
delete_front();
delete_rear();
display(); // calling display function to retrieve values after deletion
}