A Holistic Look at Circular Queues in a Data Structure

It is common to use circular queues in a data structure in operating systems. It is used to manage the execution of computing processes or programs. You use a circular queue as a buffer to store the processes in order of their insertion and then remove them at the time of resource allocation or execution. In this tutorial, you will explore a circular queue in a data structure along with its implementation and applications.

Why Was the Concept of Circular Queue Introduced?

Implementation of a linear queue brings the drawback of memory wastage. However, memory is a crucial resource that you should always protect by analyzing all the implications while designing algorithms or solutions. In the case of a linear queue, when the rear pointer reaches the MaxSize of a queue, there might be a possibility that after a certain number of dequeue() operations, it will create an empty space at the start of a queue. 

Memory_wastage_in_Linear_Queue.

Additionally, this newly created empty space can never be re-utilized as the rear pointer reaches the end of a queue. Hence, experts introduced the concept of the circular queue to overcome this limitation.

Circular_link_resolving_problem_of_MemoryWastage

As shown in the figure above, the rear pointer arrives at the beginning of a queue with the help of a circular link to re-utilize the empty space to insert a new element. This simple addition of a circular link resolves the problem of memory wastage in the case of queue implementation. Thus, this particular type of queue is considered the best version of a queue data structure. 

Full Stack Web Developer Course

To become an expert in MEAN StackView Course
Full Stack Web Developer Course

What is Circular Queue in a Data Structure?

A circular queue is an extended version of a linear queue as it follows the First In First Out principle with the exception that it connects the last node of a queue to its first by forming a circular link. Hence, it is also called a Ring Buffer.

Circular_queue_representation

As shown in the illustration above, the circular queue resolves the memory wastage problem with the help of a circular link.

Representation of Circular Queue using Arrays and a Linked List

You can implement the circular queue using both the 1-D array and the Linked list. However, implementing a circular link is a new addition that you need to execute. Additionally, this queue works by the process of circular incrementation. That is, when you reach the end of a queue, you start from the beginning of a queue. The circular incrementation is achievable with the help of the modulo division.

Now you will understand how you can achieve circular incrementation, with the help of an example. Let’s say the MaxSize of your queue is 5, and the rear pointer has already reached the end of a queue. There is one empty space at the beginning of a queue, which means that the front pointer is pointing to location 1.

Rear + 1 = 4 + 1 = 5 (Overflow Error)

Rear = (Rear + 1)% MaxSize = 0 (Reached loc. 0 / Beginning of queue)

Circular_incrementation_in_Circular_queue

Similarly, the tail node of a linked list can be connected to its head node by adding the address value of a head node in the reference field of the tail node.

Circular_Queue_Representation_Using_LinkedList

Steps for Implementing Queue Operations

Enqueue() and Dequeue() are the primary operations of the queue, which allow you to manipulate the data flow. These functions do not depend on the number of elements inside the queue or its size; that is why these operations take constant execution time, i.e., O(1) [time-complexity]. Here, you will deal with steps to implement queue operations:

1. Enqueue(x) Operation

You should follow the following steps to insert (enqueue) a data element into a circular queue -

  • Step 1: Check if the queue is full (Rear + 1 % Maxsize = Front)
  • Step 2: If the queue is full, there will be an Overflow error
  • Step 3: Check if the queue is empty, and set both Front and Rear to 0
  • Step 4: If Rear = Maxsize - 1 & Front != 0 (rear pointer is at the end of the queue and front is not at 0th index), then set Rear = 0
  • Step 5: Otherwise, set Rear = (Rear + 1) % Maxsize
  • Step 6: Insert the element into the queue (Queue[Rear] = x)
  • Step 7: Exit

Now, you will explore the Enqueue() operation by analyzing different cases of insertion in the circular queue:

Insertion_in_empty_queue.

insertion_at_beginning

2. Dequeue() Operation

Obtaining data from the queue comprises two subtasks: access the data where the front is pointing and remove the data after access. You should take the following steps to remove data from a circular queue - 

  • Step 1: Check if the queue is empty (Front = -1 & Rear = -1)
  • Step 2: If the queue is empty, Underflow error
  • Step 3: Set Element = Queue[Front]
  • Step 4: If there is only one element in a queue, set both Front and Rear to -1 (IF Front = Rear, set Front = Rear = -1)
  • Step 5: And if Front = Maxsize -1 set Front = 0
  • Step 6: Otherwise, set Front = Front + 1
  • Step 7: Exit

Let’s understand Dequeue() operation through a diagrammatic representation:

Deletion_illustration

Dequeue_from_beginning.

Implementation of Circular Queue using an Array

The process of array implementation starts with the declaration of array and pointer variables. You use the following statements for it:

#define MAX_SIZE 5    //global value assignment to max_size  variable

 int a[MAX_SIZE];

 int front = -1;

 int rear = -1;

After this, you will begin with the implementation of circular queue operations. 

1. The first primary queue operation that allows you to manage data flow in a queue is Enqueue(). For implementing this function, you have to check if the queue is empty. If a circular queue is empty, you must set both the front and rear pointer to 0 manually. This condition can be implemented as follows:

void enqueue(int x)

 {

if (front == -1 && rear == -1)

{

front = rear = 0;

}

Later, you will check if the queue is full. If it’s full, you will return an overflow error (i.e., no empty space for insertion).

else if ((rear + 1) % MAX_SIZE == front)

{

printf("queue is full\n");

return;

}

Otherwise, you must increment the rear pointer using the technique of circular incrementation. And as the rear pointer is incremented now, you can insert an element at the new location.

else

rear = (rear + 1) % MAX_SIZE;

a[rear] = x;

}

2. The next operation is Dequeue(). The Dequeue() operation removes the element from the front node of a queue.  Further, an element can only be deleted when there is at least an element to delete, i.e., Rear > 0 (queue shouldn’t be empty). If the queue is empty, then you must write an underflow error and exit.

void dequeue()

 {

if (front == -1 && rear == -1)

{

printf("queue is empty \n");

return;

}

Additionally, if there is only one element in the queue, you should set both the front and rear pointer to NULL.

else if (front == rear)

{

front = rear = -1;

}

Otherwise, you will simply increment the front pointer using the technique of circular incrementation. And as the front pointer is incremented, the link to the previous element’s memory address gets removed, resulting in the deletion of a previous element.

else

front = (front + 1) % MAX_SIZE;

}

The complete program for the array implementation of a circular queue in programming language C is given below. The code consists of two additional functions Peek() and Print(). The Peek() function returns the element at the front node of a queue without deleting it. Meanwhile, the Print() function is created to visualize the state of a queue after each operation.

Program in C:

#include <stdio.h>

#include <Windows.h>

#define MAX_SIZE 5

int a[MAX_SIZE];

int front = -1;

int rear = -1;

void enqueue(int x)

{

if (front == -1 && rear == -1)

{

front = rear = 0;

}

else if ((rear + 1) % MAX_SIZE == front)

{

printf("queue is full\n");

return;

}

else

rear = (rear + 1) % MAX_SIZE;

a[rear] = x;

}

void dequeue()

{

if (front == -1 && rear == -1)

{

printf("queue is empty \n");

return;

}

else if (front == rear)

{

front = rear = -1;

}

else

front = (front + 1) % MAX_SIZE;

}

int Peek()

{

if (a[front] == -1)

return -1;

return a[front];

}

void Print()

{

int count = ((rear + MAX_SIZE - front) % MAX_SIZE)+1;

int i;

for (i = 0; i < count; i++)

{

printf("%d ", a[(front+i)%MAX_SIZE]);

}

printf("\n");

}

int main(void)

{

enqueue(5);

printf("\nFirst insertion in circular Queue\n");

Print();

printf("\n Second insertion in circular Queue\n");

enqueue(7);

Print();

printf("\n Third insertion in circular Queue\n");

enqueue(-3);

Print();

printf("\n Fourth insertion in circular Queue\n");

enqueue(9);

Print();

printf("\n Circular Queue after first deletion\n");

dequeue();

Print();

printf("\n Circular Queue after 2nd deletion\n");

dequeue();

Print();

printf("\n Insertion in circular Queue\n");

enqueue(14);

system("pause");

return 0;

}

Output:

Circular_queue_C_Program_output.

FREE Java Certification Training

Learn A-Z of Java like never beforeEnroll Now
FREE Java Certification Training

Implementation of a Circular Queue Using a Linked List

The linked list implementation of a circular queue begins with the declaration of a node and pointer variables. For the creation of a node, you will use structure in C as follows:

 // Declaration of node using structure in C programming 

  struct node  

  {  

int data;  

struct node *next;  

  };  

  struct node *front=-1;  

  struct node *rear=-1;  

Implement circular queue operation now.

  1.  First, you must implement Enqueue() operation. In this function, you will allocate the memory for a new node using the following snippet of code:

 void enqueue(int x)  

  {  

struct node *newnode; 

newnode=(struct node *)malloc(sizeof(struct node));  //malloc function helps in allocating memory

After creating a new node, you must also insert values into both data and reference fields. Data will be an argument provided by the user, and the reference field will be set to null initially.

newnode->data=x;

     newnode->next=0;  

There are two conditions for inserting a new node into the linked circular queue.

In the first condition, you will insert a new node into an empty queue. In this case, both the front and rear pointer must be Null. You must check it using the condition rear == -1. If it is true, the new element will be added as the first element of the queue, and both the front and the rear pointer will also be updated to point to this new node. 

   if(rear == -1){

  front = rear = newnode;

  Rear->next = front 

    } 

In the second case, the queue already contains over one data element. Now, the condition rear == -1 becomes false. In this scenario, you will just update the rear pointer to point to the new node. You should also change the address field of the previous node to establish a new link.

    else  

{  

      rear->next=newnode;  

      rear=newnode;  

      rear->next=front;  

 }  

2. The dequeue() operation is accountable for deleting the data element from the front node of a queue. While implementing this operation, you will first check if the queue is empty as you cannot delete the element if there is no element in a queue.

  void dequeue()  

   {  

   struct node *temp;   // declaration of pointer node  

   temp=front;  

   if((front==-1)&&(rear==-1))   

    {  

        printf("\nQueue is empty");  

    }  

Further, if there is only one element in the queue, you must set both the front and the rear pointer to NULL.

        else if(front==rear)  

   {  

       front=rear=-1;  

       free(temp);  

   }  

Otherwise, you must update the value of the front pointer to point to the next node. And it will =deallocate the memory of deleted node with the help of the free() function in C.

        else  

   {  

       front=front->next;  

       rear->next=front;  

       free(temp);  

   } 

The linked list implementation of a circular queue using the C programming language is given below. The program contains one additional function named Display().  In this function, you have initialized the temp pointer pointing to the front node. And with the help of the while condition, you will traverse through the whole circular linked queue.

Program in C:

#include <stdio.h> 

#include<stdlib.h>   

struct Node {

int data;

struct Node* next;

};

struct Node* front = NULL;

struct Node* rear = NULL;

void enqueue(int x)  

{  

    struct Node* newnode = 

(struct Node*)malloc(sizeof(struct Node));

    newnode->data=x;  

    newnode->next=0;  

    if(rear== NULL)   

    {  

        front=rear=newnode;  

        rear->next=front;  

    }  

    else  

    {  

        rear->next=newnode;  

        rear=newnode;  

        rear->next=front;  

    }  

}  

void dequeue()  

{  

    struct Node* temp = front;  

    if((front==NULL)&&(rear==NULL))    

    {  

        printf("\nQueue is empty");  

    }  

    else if(front==rear) 

    {  

        front=rear=NULL;  

        free(temp);  

    }  

    else  

    {  

        front=front->next;  

        rear->next=front;  

        free(temp);  

    }  

}   

int peek()  

{  

    if((front==NULL) &&(rear==NULL))  

    {  

        printf("\nQueue is empty");  

    }  

    else  

    {  

        printf("\nThe front element is %d", front->data);  

    }  

}    

void display()  

{  

    struct Node* temp = front;  

    printf("\n The elements in a Queue are : ");  

    if((front==NULL) && (rear==NULL))  

    {  

        printf("Queue is empty");  

    }  

  

    else   

    {  

        while(temp->next!=front)  

        {  

            printf("%d,", temp->data);  

            temp=temp->next;  

        }  

        printf("%d", temp->data);  

    }  

}  

int main()  

{  

    enqueue(34);   

    enqueue(10);  

    enqueue(23);  

    display();   

    dequeue();   

    peek();  

}

Output:

Result_circular_queue_using_linked_list

Applications of a Circular Queue

  • Buffer in Computer Systems: Computer systems supply a holding area for maintaining communication between two processes, two programs, or even two systems. This memory area is also known as a ring buffer.

  • CPU Scheduling: In the Round-Robin Scheduling Algorithm, a circular queue is utilized to maintain processes that are in a ready state.

  • Traffic System: Circular queue is also utilized in traffic systems that are controlled by computers. Each traffic light turns ON in a circular incrementation pattern in a constant interval of time.

Get a firm foundation in Java, the most commonly used programming language in software development with the Java Certification Training Course.

Conclusion

In this tutorial, you explored the circular queues in a data structure. A circular queue resolves memory wastage drawback, which is faced in the implementation of a linear queue. You also went through the steps to implement primary circular queue operations. Following this, you understood the development of queue operations with the help of a drive-through coding explanation. You also encountered the coding implementation of queue, using a linked list and an array with the help of the C programming language.

If you are looking for more comprehensive learning that goes beyond data structures and covers the most in-demand programming languages and skills needed today to build interactive applications, Simplilearn’s Software Development Courses will prove to be just right for you. 

In this category, you will find Software Development Post Graduate Programs, Master’s Programs, and Certification Courses. You can also choose courses like SQL training, React JS, Salesforce Administrator, and App Builder, Java Certification Training, Python Training, etc. The courses mentioned in the catalog above will help you master the art of Software Development and become job-ready. Explore now and get started!

On that note, do you have any questions related to this tutorial on a circular queue? If you do, please don’t hesitate to place them as comments at the end of this page; we will respond to them soon!

About the Author

SimplilearnSimplilearn

Simplilearn is one of the world’s leading providers of online training for Digital Marketing, Cloud Computing, Project Management, Data Science, IT, Software Development, and many other emerging technologies.

View More
  • Disclaimer
  • PMP, PMI, PMBOK, CAPM, PgMP, PfMP, ACP, PBA, RMP, SP, and OPM3 are registered marks of the Project Management Institute, Inc.