[Medium] 622. Design Circular Queue
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 use the space to store new values.
Implementation the MyCircularQueue class:
MyCircularQueue(int k)Initializes the object with the size of the queue to bek.boolean enQueue(int value)Inserts an element into the circular queue. Returntrueif the operation is successful.boolean deQueue()Deletes an element from the circular queue. Returntrueif the operation is successful.int Front()Gets the front item from the queue. If the queue is empty, return-1.int Rear()Gets the last item from the queue. If the queue is empty, return-1.boolean isEmpty()Checks whether the circular queue is empty or not.boolean isFull()Checks whether the circular queue is full or not.
You must solve the problem without using the built-in queue data structure in your programming language.
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]
Explanation
MyCircularQueue myCircularQueue = new MyCircularQueue(3);
myCircularQueue.enQueue(1); // return True
myCircularQueue.enQueue(2); // return True
myCircularQueue.enQueue(3); // return True
myCircularQueue.enQueue(4); // return False (queue is full)
myCircularQueue.Rear(); // return 3
myCircularQueue.isFull(); // return True
myCircularQueue.deQueue(); // return True
myCircularQueue.enQueue(4); // return True
myCircularQueue.Rear(); // return 4
Constraints
1 <= k <= 10000 <= value <= 1000- At most
3000calls will be made toenQueue,deQueue,Front,Rear,isEmpty, andisFull.
Thinking Process
- Circular Array Approach:
- Uses modulo arithmetic for wrapping:
(index + 1) % cap - Tracks
sizeto distinguish empty vs full - More memory efficient (fixed array)
- Uses modulo arithmetic for wrapping:
- Draw pointers before rewriting links.
- Dummy head simplifies insert/delete at the head.
- Slow/fast pointers find middle or detect cycles in one pass.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Iterative pointer walk (this problem) | O(n) | O(1) | Traversal, insertion |
| Dummy head node | O(n) | O(1) | Simplify head-edge cases |
| Reversal (3-pointer) | O(n) | O(1) | Reverse sublist or full list |
| Slow/fast pointers | O(n) | O(1) | Middle, cycle, merge lists |
Solution
class MyCircularQueue {
public:
MyCircularQueue(int k) : q(k), head(0), tail(0), size(0), cap(k) {
}
bool enQueue(int value) {
if(isFull()) return false;
q[tail] = value;
tail = (tail + 1) % cap;
size++;
return true;
}
bool deQueue() {
if(isEmpty()) return false;
head = (head + 1) % cap;
size--;
return true;
}
int Front() {
return isEmpty() ? -1 : q[head];
}
int Rear() {
return isEmpty()? -1 : q[(tail - 1 + cap) % cap];
}
bool isEmpty() {
return size == 0;
}
bool isFull() {
return size == cap;
}
private:
vector<int> q;
int head, tail, size, cap;
};
/**
* Your MyCircularQueue object will be instantiated and called as such:
* MyCircularQueue* obj = new MyCircularQueue(k);
* bool param_1 = obj->enQueue(value);
* bool param_2 = obj->deQueue();
* int param_3 = obj->Front();
* int param_4 = obj->Rear();
* bool param_5 = obj->isEmpty();
* bool param_6 = obj->isFull();
*/
Solution Explanation
Approach: Iterative pointer walk (this problem)
Key idea: 1. Circular Array Approach:
How the code works:
- Circular Array Approach:
- Uses modulo arithmetic for wrapping:
(index + 1) % cap - Tracks
sizeto distinguish empty vs full - More memory efficient (fixed array)
- Draw pointers before rewriting links.
- Dummy head simplifies insert/delete at the head.
Common Mistakes
- Uses modulo arithmetic for wrapping:
- Empty queue: All operations return
-1orfalseexceptisEmpty()→true - Full queue:
enQueue()returnsfalse - Single element: After
deQueue(), queue becomes empty - Capacity 1: Only one element can be stored
-
Wrap around: Array approach wraps
tailfromcap-1to0 - Not tracking size: Using only
headandtailcan’t distinguish empty from full - Wrong modulo calculation:
(tail - 1 + cap) % capfor rear (nottail - 1) - Memory leaks: Not deleting nodes in linked list
deQueue() - Null pointer: Not checking
tail == nullptrafterdeQueue()when empty - Index out of bounds: Not using modulo for array indices
Related Problems
- LC 641: Design Circular Deque - Circular queue with both ends
- LC 232: Implement Queue using Stacks - Queue implementation
- LC 225: Implement Stack using Queues - Stack implementation
- LC 146: LRU Cache - Another design problem
Key Takeaways
- Circular Array Approach:
- Uses modulo arithmetic for wrapping:
(index + 1) % cap - Tracks
sizeto distinguish empty vs full - More memory efficient (fixed array)
- Uses modulo arithmetic for wrapping:
- Linked List Approach:
- Dynamic node allocation
- Simpler logic (no modulo needed)
- Requires memory management (delete nodes)
- Empty vs Full Distinction:
- Array: Use
sizecounter (not just head/tail positions) - Linked List: Use
cntcounter
- Array: Use
- Rear Element:
- Array:
q[(tail - 1 + cap) % cap](previous position, wrapped) - Linked List:
tail->val(direct access)
- Array:
References
- LC 622: Design Circular Queue on LeetCode
- LeetCode Discuss — LC 622: Design Circular Queue
- LeetCode Editorial (may require premium)