
In linear queues, insertion occurs at the rear end, while deletion happens at the front end.
Ex:
10 | 20 | 30 | 40 | 50 |
F=0, R=4
If we want to insert a new element in the queue, it is impossible because the queue is full.
Delete the elements 10 and 20
F=2 R=4
Even though some space is available, we cannot insert a new element because it violates the condition R=MAX-1. This is the major drawback of the linear queue.
To overcome this problem we use the concept of circular queue.
The linear queue in a circle is called a circular queue.
The rules and regulations we apply for queues will also apply to circular queues. In a circular queue, the first index comes right after the last.
Table of Contents
How to Implement a Circular Queue Using an Array:
- Initialize the Circular Queue
- Step 1: Create an array of fixed-size maxSize to hold the queue elements.
- Step 2: Set two pointers, Front and Rear, to indicate the start and end of the queue. Initialize both Front and Rear to -1, indicating an empty queue.
- Check for Queue Overflow
- Step 3: Ensure the queue is not full before inserting an element. This occurs when (Rear + 1) % maxSize == Front. If true, print “Queue Overflow” and stop the insertion process.
- Insert an Element
- Step 4: Update the Rear pointer. If Rear == maxSize – 1, set Rear to 0 (circular wrap-around). Otherwise, increment Rear by 1.
- Step 5: Insert the new item at Q[Rear].
- Step 6: If Front is -1 (the queue was empty), set Front to 0 to mark the queue as not empty.
- Check for Queue Underflow
- Step 7: Check if the queue is empty before deleting an element. This occurs when Front == -1. If true, print “Queue Underflow” and stop the deletion process.
- Delete an Element
- Step 8: Store the item at Q[Front] to be returned.
- Step 9: Update the Front pointer. If Front == Rear, reset both Front and Rear to -1 (the queue is empty).
- Step 10: If Front == maxSize – 1, set Front to 0 (circular wrap-around). Otherwise, increment Front by 1.
- Return the Deleted Element
- Step 11: Return the item stored from the front of the queue.
Insertion into a Circular Queue Algorithm:
CQueueInsertion(Q, maxSize, Front, Rear, item)
Step 1:
- If Rear == maxSize – 1, then
- Set Rear = 0
- Else,
- Increment Rear by 1 (Rear = Rear + 1)
Step 2:
- If Front == Rear, then
- Print “Queue Overflow”
- Return
Step 3:
- Set Q[Rear] = item
Step 4:
- If Front == 0, then
- Set Front = 1
Step 5:
- Return
Deletion from Circular Queue Algorithm:
CQueueDeletion(Q,maxSize,Front,Rear,item)
Step 1:
- Check for Queue Underflow:
- If Front == 0, then
- Print “Queue Underflow”
- Return
(If the front index is 0, the queue is empty and there’s nothing to delete.)
- If Front == 0, then
Step 2:
- Store the Front Item:
- Set K = Q[Front]
(Retrieve the item at the front of the queue.)
- Set K = Q[Front]
Step 3:
- Check if the Queue is Now Empty:
- If Front == Rear, then
- Begin
- Set Front = -1
- Set Rear = -1
- End
(If front equals rear, this is the last item in the queue, so we reset both front and rear to -1, indicating the queue is now empty.)
- Begin
- If Front == Rear, then
- Else:
- Update Front for Circular Movement:
- If Front == maxSize – 1, then
- Set Front = 0
(If the front is at the last position in the array, move it to the beginning, maintaining the circular nature of the queue.)
- Set Front = 0
- Else
- Set Front = Front + 1
(If not, advance the front pointer to the next position.)
- Set Front = Front + 1
- If Front == maxSize – 1, then
- Update Front for Circular Movement:
Step 4:
- Return the Deleted Item:
- Return K
(The queue removes and returns the front element.)
- Return K
Program Implementation of a Circular Queue:
class CircularQueue: def __init__(self, size): self.size = size # Maximum size of the queue self.queue = [None] * size # Initialize the queue with None values self.front = self.rear = -1 # Initialize front and rear pointers to -1 def is_empty(self): # Check if the queue is empty return self.front == -1 def is_full(self): # Check if the queue is full return (self.rear + 1) % self.size == self.front def enqueue(self, data): # Function to insert an element to the queue if self.is_full(): print("Queue is full!") return if self.front == -1: # If the queue is empty, set front to 0 self.front = 0 self.rear = (self.rear + 1) % self.size # Move rear pointer circularly self.queue[self.rear] = data # Insert data at the rear def dequeue(self): # Function to delete an element from the queue if self.is_empty(): print("Queue is empty!") return data = self.queue[self.front] if self.front == self.rear: # If only one element is present, reset front and rear self.front = self.rear = -1 else: self.front = (self.front + 1) % self.size # Move front pointer circularly return data # Return the deleted element def print_queue(self): # Function to print the elements of the queue if self.is_empty(): print("Queue is empty!") return if self.rear >= self.front: # Print elements from front to rear if rear is ahead for i in range(self.front, self.rear + 1): print(self.queue[i], end=" ") print() else: # Print elements from front to the end of the array for i in range(self.front, self.size): print(self.queue[i], end=" ") # Print elements from the beginning of the array to rear for i in range(0, self.rear + 1): print(self.queue[i], end=" ") print() # Example usage queue = CircularQueue(5) queue.enqueue(1) queue.enqueue(2) queue.enqueue(3) queue.enqueue(4) queue.enqueue(5) print("Queue:") queue.print_queue() # Output: 1 2 3 4 5 queue.dequeue() queue.dequeue() print("Queue after two dequeues:") queue.print_queue() # Output: 3 4 5 queue.enqueue(6) queue.enqueue(7) print("Queue after two enqueues:") queue.print_queue() # Output: 3 4 5 6 7
#include <iostream> using namespace std; class CircularQueue { private: int size; // Maximum size of the queue int *queue; // Array to store queue elements int front; // Index of the front element int rear; // Index of the rear element public: // Constructor CircularQueue(int queueSize) : size(queueSize), front(-1), rear(-1) { queue = new int[size]; // Allocate memory for the queue array } // Destructor to free allocated memory ~CircularQueue() { delete[] queue; } // Check if the queue is empty bool isEmpty() { return (front == -1); } // Check if the queue is full bool isFull() { return ((rear + 1) % size == front); } // Enqueue operation: Insert an element at the rear void enqueue(int value) { if (isFull()) { cout << "Queue Overflow\n"; } else { if (isEmpty()) { front = rear = 0; // Set both front and rear to 0 if the queue is empty } else { rear = (rear + 1) % size; // Move rear pointer circularly } queue[rear] = value; // Insert the value at the rear cout << "Inserted " << value << " into the queue.\n"; } } // Dequeue operation: Remove an element from the front int dequeue() { int value; if (isEmpty()) { cout << "Queue Underflow\n"; return -1; // Return -1 to indicate underflow } else { value = queue[front]; if (front == rear) { // If only one element is present front = rear = -1; // Reset front and rear } else { front = (front + 1) % size; // Move front pointer circularly } return value; } } // Function to display the elements of the queue void display() { if (isEmpty()) { cout << "Queue is Empty\n"; return; } cout << "Elements in Circular Queue are: "; int i = front; do { cout << queue[i] << " "; i = (i + 1) % size; } while (i != (rear + 1) % size); cout << endl; } }; int main() { CircularQueue q(5); // Create a circular queue of size 5 q.enqueue(1); q.enqueue(2); q.enqueue(3); q.enqueue(4); q.enqueue(5); // Queue is now full: 1 2 3 4 5 cout << "Queue: "; q.display(); // Output: 1 2 3 4 5 q.dequeue(); q.dequeue(); // Two elements dequeued cout << "Queue after two dequeues: "; q.display(); // Output: 3 4 5 q.enqueue(6); q.enqueue(7); // Two elements enqueued cout << "Queue after two enqueues: "; q.display(); // Output: 3 4 5 6 7 return 0; }
class CircularQueue { private int size; // Maximum size of the queue private int[] queue; // Array to store queue elements private int front; // Index of the front element private int rear; // Index of the rear element // Constructor public CircularQueue(int queueSize) { this.size = queueSize; this.queue = new int[size]; this.front = -1; this.rear = -1; } // Check if the queue is empty public boolean isEmpty() { return (front == -1); } // Check if the queue is full public boolean isFull() { return ((rear + 1) % size == front); } // Enqueue operation: Insert an element at the rear public void enqueue(int value) { if (isFull()) { System.out.println("Queue Overflow"); } else { if (isEmpty()) { front = rear = 0; // If the queue is empty, set both front and rear to 0 } else { rear = (rear + 1) % size; // Move rear pointer circularly } queue[rear] = value; // Insert the value at the rear System.out.println("Inserted " + value + " into the queue."); } } // Dequeue operation: Remove an element from the front public int dequeue() { int value; if (isEmpty()) { System.out.println("Queue Underflow"); return -1; // Return -1 to indicate underflow } else { value = queue[front]; if (front == rear) { // If only one element is present front = rear = -1; // Reset front and rear } else { front = (front + 1) % size; // Move front pointer circularly } return value; } } // Function to display the elements of the queue public void display() { if (isEmpty()) { System.out.println("Queue is Empty"); return; } System.out.print("Elements in Circular Queue are: "); int i = front; do { System.out.print(queue[i] + " "); i = (i + 1) % size; } while (i != (rear + 1) % size); System.out.println(); } public static void main(String[] args) { CircularQueue q = new CircularQueue(5); // Create a circular queue of size 5 q.enqueue(1); q.enqueue(2); q.enqueue(3); q.enqueue(4); q.enqueue(5); // Queue is now full: 1 2 3 4 5 System.out.println("Queue:"); q.display(); // Output: 1 2 3 4 5 q.dequeue(); q.dequeue(); // Two elements dequeued System.out.println("Queue after two dequeues:"); q.display(); // Output: 3 4 5 q.enqueue(6); q.enqueue(7); // Two elements enqueued System.out.println("Queue after two enqueues:"); q.display(); // Output: 3 4 5 6 7 } }
#include <stdio.h> #include <stdlib.h> #define SIZE 5 // Define the maximum size of the queue // Structure to represent a circular queue typedef struct { int items[SIZE]; // Array to store queue elements int front; // Index of the front element int rear; // Index of the rear element } CircularQueue; // Function to initialize the circular queue void initializeQueue(CircularQueue *q) { q->front = -1; q->rear = -1; } // Function to check if the queue is empty int isEmpty(CircularQueue *q) { return (q->front == -1); } // Function to check if the queue is full int isFull(CircularQueue *q) { return ((q->rear + 1) % SIZE == q->front); } // Function to enqueue (insert) an element into the queue void enqueue(CircularQueue *q, int value) { if (isFull(q)) { printf("Queue Overflow\n"); } else { if (isEmpty(q)) { q->front = 0; // If the queue is empty, set front to 0 } q->rear = (q->rear + 1) % SIZE; // Move rear pointer circularly q->items[q->rear] = value; // Insert the value at the rear printf("Inserted %d into the queue.\n", value); } } // Function to dequeue (remove) an element from the queue int dequeue(CircularQueue *q) { int value; if (isEmpty(q)) { printf("Queue Underflow\n"); return -1; // Return -1 to indicate underflow } else { value = q->items[q->front]; if (q->front == q->rear) { q->front = q->rear = -1; // Reset front and rear if only one element was present } else { q->front = (q->front + 1) % SIZE; // Move front pointer circularly } return value; } } // Function to display the elements of the queue void display(CircularQueue *q) { if (isEmpty(q)) { printf("Queue is Empty\n"); } else { int i = q->front; printf("Elements in Circular Queue are: "); do { printf("%d ", q->items[i]); i = (i + 1) % SIZE; } while (i != (q->rear + 1) % SIZE); // Loop until the rear is reached printf("\n"); } } int main() { CircularQueue q; initializeQueue(&q); // Initialize the queue enqueue(&q, 1); enqueue(&q, 2); enqueue(&q, 3); enqueue(&q, 4); enqueue(&q, 5); // Queue is now full: 1 2 3 4 5 printf("Queue: "); display(&q); // Output: 1 2 3 4 5 dequeue(&q); dequeue(&q); // Two elements dequeued printf("Queue after two dequeues: "); display(&q); // Output: 3 4 5 enqueue(&q, 6); enqueue(&q, 7); // Two elements enqueued printf("Queue after two enqueues: "); display(&q); // Output: 3 4 5 6 7 return 0; }
Output:
Inserted 1 into the queue.
Inserted 2 into the queue.
Inserted 3 into the queue.
Inserted 4 into the queue.
Inserted 5 into the queue.
Queue: Elements in Circular Queue are: 1 2 3 4 5
Queue after two dequeues: Elements in Circular Queue are: 3 4 5
Inserted 6 into the queue.
Inserted 7 into the queue.
Queue after two enqueues: Elements in Circular Queue are: 3 4 5 6 7
Complexity Analysis of Circular Queue Using Array
Operation | Time Complexity (Average) | Time Complexity (Worst) | Space Complexity |
Insertion (Enqueue) | O(1) | O(1) | – |
Deletion (Dequeue) | O(1) | O(1) | – |
IsEmpty() | O(1) | O(1) | – |
IsFull() | O(1) | O(1) | – |
Space Used | – | – | O(n) |
Applications of Circular Queues:
- CPU Scheduling: Circular queues enable fair and efficient allocation of CPU time to multiple processes, preventing any single process from monopolizing resources.
- Data Buffering: Circular queues function as temporary data holders in streaming services and multimedia applications, ensuring smooth and uninterrupted playback.
- Keyboard Buffering: When typing speed exceeds processing speed, circular queues store keystrokes, preventing loss and ensuring they are processed in the correct order.
- Memory Management: Certain memory allocation strategies utilize circular queues to efficiently manage free memory blocks, optimizing allocation and deallocation processes.
- Network Packet Handling: Routers frequently use circular queues to handle incoming data packets, ensuring fair and efficient processing and transmission across networks.
Related FAQs
What is a Circular Queue?
A circular queue is a variation of a regular queue where the last element points back to the beginning, allowing for efficient use of space.
How is a Circular Queue Implemented?
Circular queues are typically implemented using an array, with front and rear pointers managing the queue’s head and tail.
What are the Advantages of a Circular Queue?
Circular queues offer efficient memory usage and prevent overflow if implemented correctly.
When to Use a Circular Queue?
Use circular queues for CPU scheduling, buffering data streams, or managing circular buffers.
How do you know if a circular queue is full or empty?
Check for fullness by comparing rear and front indices, accounting for circularity.