Queue & Circular Queue


Queue
Queue is also an abstract data type or a linear data structure, just like stack data structure, in which the first element is inserted from one end called the REAR(also called tail), and the removal of existing element takes place from the other end called as FRONT(also called head).
This makes queue as FIFO(First in First Out) data structure, which means that element inserted first will be removed first.
Which is exactly how queue system works in real world. If you go to a ticket counter to buy movie tickets, and are first in the queue, then you will be the first one to get the tickets. Right? Same is the case with Queue data structure. Data inserted first, will leave the queue first.
The process to add an element into queue is called Enqueue and the process of removal of an element from queue is called Dequeue.

Basic features of Queue
1.     Like stack, queue is also an ordered list of elements of similar data types.
2.    Queue is a FIFO( First in First Out ) structure.
3.    Once a new element is inserted into the Queue, all the elements inserted before the new element in the queue must be removed, to remove the new element.
4.    peek( ) function is oftenly used to return the value of first element without dequeuing it.
Applications of Queue
Queue, as the name suggests is used whenever we need to manage any group of objects in an order in which the first one coming in, also gets out first while the others wait for their turn, like in the following scenarios:
1.     Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
2.    In real life scenario, Call Center phone systems uses Queues to hold people calling them in an order, until a service representative is free.
3.    Handling of interrupts in real-time systems. The interrupts are handled in the same order as they arrive i.e First come first served.
Implementation of Queue Data Structure
Queue can be implemented using an Array, Stack or Linked List. The easiest way of implementing a queue is by using an Array.
Initially the head(FRONT) and the tail(REAR) of the queue points at the first index of the array (starting the index of array from 0). As we add elements to the queue, the tail keeps on moving ahead, always pointing to the position where the next element will be inserted, while the head remains at the first index.
When we remove an element from Queue, we can follow two possible approaches (mentioned [A] and [B] in above diagram). In [A] approach, we remove the element at head position, and then one by one shift all the other elements in forward position.
In approach [B] we remove the element from head position and then move head to the next position.
In approach [A] there is an overhead of shifting the elements one position forward every time we remove the first element.
In approach [B] there is no such overhead, but whenever we move head one position ahead, after removal of first element, the size on Queue is reduced by one space each time.


Algorithm for ENQUEUE operation
1.     Check if the queue is full or not.
2.    If the queue is full, then print overflow error and exit the program.
3.    If the queue is not full, then increment the tail and add the element.
Algorithm for DEQUEUE operation
1.     Check if the queue is empty or not.
2.    If the queue is empty, then print underflow error and exit the program.
3.    If the queue is not empty, then print the element at the head and increment the head.

Circular queue
It avoids the wastage of space in a regular queue implementation using array  
As you can see in the above image, after a bit of enqueueing and dequeueing, the size of the queue has been reduced.
The indexes 0 and 1 can only be used after the queue is reset when all the elements have been dequeued.

How Circular Queue Works

Circular Queue works by the process of circular increment i.e. when we try to increment any variable and we reach the end of queue, we start from the beginning of queue by modulo division with the queue size.i.e.
if REAR + 1 == 5 (overflow!), REAR = (REAR + 1)%5 = 0 (start of queue)

Queue operations work as follows:
  • Two pointers called FRONT and REAR are used to keep track of the first and last elements in the queue.
  • When initializing the queue, we set the value of FRONT and REAR to -1.
  • On enqueing an element, we circularly increase the value of REAR index and place the new element in the position pointed to by REAR.
  • On dequeueing an element, we return the value pointed to by FRONT and circularly increase the FRONT index.
  • Before enqueing, we check if queue is already full.
  • Before dequeuing, we check if queue is already empty.
  • When enqueing the first element, we set the value of FRONT to 0.
  • When dequeing the last element, we reset the values of FRONT and REAR to -1.
However, the check for full queue has a new additional case:
  • Case 1: FRONT = 0 && REAR == SIZE - 1
  • Case 2: FRONT = REAR + 1
The second case happens when REAR starts from 0 due to circular increment and when its value is just 1 less than FRONT, the queue is full.

Implementation using C programming
#include <stdio.h>

#define SIZE 5

int items[SIZE];
int front = -1, rear =-1;
int isFull()
{
    if( (front == rear + 1) || (front == 0 && rear == SIZE-1)) return 1;
    return 0;
}

int isEmpty()
{
    if(front == -1) return 1;
    return 0;
}

void enQueue(int element)
{
    if(isFull()) printf("\n Queue is full!! \n");
    else
    {
        if(front == -1) front = 0;
        rear = (rear + 1) % SIZE;
        items[rear] = element;
        printf("\n Inserted -> %d", element);
    }
}


int deQueue()
{
    int element;
    if(isEmpty()) {
        printf("\n Queue is empty !! \n");
        return(-1);
    } else {
        element = items[front];
        if (front == rear){
            front = -1;
            rear = -1;
        } /* Q has only one element, so we reset the queue after dequeing it. ? */
        else {
            front = (front + 1) % SIZE;
           
        }
        printf("\n Deleted element -> %d \n", element);
        return(element);
    }
}


void display()
{
    int i;
    if(isEmpty()) printf(" \n Empty Queue\n");
    else
    {
        printf("\n Front -> %d ",front);
        printf("\n Items -> ");
        for( i = front; i!=rear; i=(i+1)%SIZE) {
            printf("%d ",items[i]);
        }
        printf("%d ",items[i]);
        printf("\n Rear -> %d \n",rear);
    }
}

int main()
{
    // Fails because front = -1
    deQueue();
   
    enQueue(1);
    enQueue(2);
    enQueue(3);
    enQueue(4);
    enQueue(5);
   
    // Fails to enqueue because front == 0 && rear == SIZE - 1
    enQueue(6);
   
    display();
    deQueue();
   
    display();
   
    enQueue(7);
    display();
   
    // Fails to enqueue because front == rear + 1
    enQueue(8);
   
    return 0;
}
When you run this program, the output will be
Queue is empty

Inserted 1

Inserted 2

Inserted 3

Inserted 4

Inserted 5
Queue is full
Front -> 0
Items -> 12345
Rear -> 4
Deleted Element is 1
Front -> 1
Items -> 2345
Rear -> 4
Inserted 7

Front -> 1
Items -> 23457
Rear -> 0

Queue is full

Popular posts from this blog

18CS45 object oriented concept (OOC) notes, question paper

python application program 15CS664 notes question paper , important question

Operation Research Notes 15CS653