Design Circular Queue
Problem Description
Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a circle. It is also called “Ring Buffer”.
One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can store the next element in those spaces.
Implementation the MyCircularQueue class:
MyCircularQueue(k)Initializes the object with the size of the queue to bek.Front()Gets the front item from the queue. If the queue is empty, return-1.Rear()Gets the last item from the queue. If the queue is empty, return-1.enQueue(value)Inserts an element into the circular queue. Returntrueif the operation is successful.deQueue()Deletes an element from the circular queue. Returntrueif the operation is successful.isEmpty()Checks whether the circular queue is empty or not.isFull()Checks whether the circular queue is full or not.
You must solve the problem without using any built-in queue libraries.
Examples
Example 1:Input: [“MyCircularQueue”, “enQueue”, “enQueue”, “enQueue”, “enQueue”, “Rear”, “isFull”, “deQueue”, “enQueue”, “Rear”] [[3], [1], [2], [3], [4], [], [], [], [4], []] Output: [null, true, true, true, false, 3, true, true, true, 4]
Constraints
1 <= k <= 10000 <= value <= 1000- At most
3000calls will be made toenQueue,deQueue,Front,Rear,isEmpty, andisFull.
Array with Head and Tail Pointers
A circular queue can be implemented using a fixed-size array of capacity k and two index pointers: head and count (number of active elements in the queue).
Using count instead of a tail pointer simplifies checking if the queue is empty (count == 0) or full (count == k).
- To find the rear element’s index:
(head + count - 1) % k. - To enqueue: compute the next write index
(head + count) % k, store the value, and incrementcount. - To dequeue: advance
head = (head + 1) % kand decrementcount. All operations execute in O(1) time with O(k) space.
Solution 1: Array-Based Ring Buffer
Utilize index arithmetic modulo k to recycle array slots.
class MyCircularQueue {
private int[] data;
private int head;
private int count;
private int capacity;
public MyCircularQueue(int k) {
data = new int[k];
head = 0;
count = 0;
capacity = k;
}
public boolean enQueue(int value) {
if (isFull()) return false;
data[(head + count) % capacity] = value;
count++;
return true;
}
public boolean deQueue() {
if (isEmpty()) return false;
head = (head + 1) % capacity;
count--;
return true;
}
public int Front() {
if (isEmpty()) return -1;
return data[head];
}
public int Rear() {
if (isEmpty()) return -1;
return data[(head + count - 1) % capacity];
}
public boolean isEmpty() {
return count == 0;
}
public boolean isFull() {
return count == capacity;
}
}class MyCircularQueue:
def __init__(self, k: int):
self.data = [0] * k
self.head = 0
self.count = 0
self.capacity = k
def enQueue(self, value: int) -> bool:
if self.isFull():
return False
self.data[(self.head + self.count) % self.capacity] = value
self.count += 1
return True
def deQueue(self) -> bool:
if self.isEmpty():
return False
self.head = (self.head + 1) % self.capacity
self.count -= 1
return True
def Front(self) -> int:
if self.isEmpty():
return -1
return self.data[self.head]
def Rear(self) -> int:
if self.isEmpty():
return -1
return self.data[(self.head + self.count - 1) % self.capacity]
def isEmpty(self) -> bool:
return self.count == 0
def isFull(self) -> bool:
return self.count == self.capacity#include <vector>
class MyCircularQueue {
private:
std::vector<int> data;
int head;
int count;
int capacity;
public:
MyCircularQueue(int k) {
data.resize(k);
head = 0;
count = 0;
capacity = k;
}
bool enQueue(int value) {
if (isFull()) return false;
data[(head + count) % capacity] = value;
count++;
return true;
}
bool deQueue() {
if (isEmpty()) return false;
head = (head + 1) % capacity;
count--;
return true;
}
int Front() {
if (isEmpty()) return -1;
return data[head];
}
int Rear() {
if (isEmpty()) return -1;
return data[(head + count - 1) % capacity];
}
bool isEmpty() {
return count == 0;
}
bool isFull() {
return count == capacity;
}
};Complexity Analysis
- Time Complexity: O(1) for all operations since array reads/writes take constant time.
- Space Complexity: O(k) auxiliary space to store elements in the pre-allocated array.
Where It Breaks
If the queue capacity must grow dynamically (unlike the fixed parameter k), we must reallocate the array and copy elements, raising the enqueue cost of resizing steps to O(n).
Common Mistakes
- Incorrect rear calculation: Computing rear as
(head + count) % kinstead of(head + count - 1) % k. - Race conditions in counts: Incrementing
countbefore writing the data value, which can write to an incorrect slot if concurrent threads access the queue.