622. Design Circular Queue - Detailed Explanation
Problem Statement
Description:
You are required to design a circular queue with a fixed size. The circular queue supports the following operations:
-
MyCircularQueue(k):
Initializes the circular queue with a maximum capacity of k. -
enQueue(value):
Inserts an element into the circular queue. Returnstrue
if the operation is successful; otherwise, returnsfalse
. -
deQueue():
Deletes an element from the circular queue. Returnstrue
if the operation is successful; otherwise, returnsfalse
. -
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
. -
isEmpty():
Checks whether the circular queue is empty. -
isFull():
Checks whether the circular queue is full.
Example 1:
- Input:
MyCircularQueue circularQueue = new MyCircularQueue(3); // set the size to be 3 circularQueue.enQueue(1); // return true circularQueue.enQueue(2); // return true circularQueue.enQueue(3); // return true circularQueue.enQueue(4); // return false, the queue is full circularQueue.Rear(); // return 3 circularQueue.isFull(); // return true circularQueue.deQueue(); // return true circularQueue.enQueue(4); // return true circularQueue.Rear(); // return 4
- Output:
The operations should behave as described above.
Constraints:
- 1 ≤ k ≤ 1000 (or similar bounds)
- All values inserted will be integers.
- The operations must work in O(1) time on average.
Hints
-
Hint 1:
Think of the queue as a fixed-size array. Use two pointers (or indices) to track the front and rear positions. -
Hint 2:
To support wrap-around, use modulo arithmetic when incrementing your pointers. This will help you simulate a circular structure within a linear array. -
Hint 3:
Be careful when checking if the queue is empty or full. One common trick is to maintain a count of the current number of elements or reserve one slot in the array to distinguish between full and empty cases.
Approach 1: Using an Array with Head, Tail, and Count
Idea
-
Data Structure:
Use a fixed-size array to store the elements. Maintain three variables:head
: The index of the front element.tail
: The index where the next element will be inserted.count
: The current number of elements in the queue.
-
Operations:
-
enQueue(value):
If the queue is full (count == capacity
), returnfalse
. Otherwise, insert the value at thetail
index, then incrementtail
(using modulo arithmetic to wrap around) and increase thecount
. -
deQueue():
If the queue is empty (count == 0
), returnfalse
. Otherwise, remove the element at thehead
, then incrementhead
(using modulo arithmetic) and decrease thecount
. -
Front():
Return the element athead
if not empty; otherwise, return-1
. -
Rear():
The last element is at index(tail - 1 + capacity) % capacity
if the queue is not empty. -
isEmpty() / isFull():
Use thecount
variable to check if the queue is empty or full.
-
Walkthrough Example
Let’s simulate operations on a circular queue of capacity 3:
-
Initialize:
head = 0
,tail = 0
,count = 0
, and an array of size 3.
-
enQueue(1):
- Insert
1
attail
index 0. - Set
tail = (0 + 1) % 3 = 1
andcount = 1
.
- Insert
-
enQueue(2):
- Insert
2
attail
index 1. - Set
tail = (1 + 1) % 3 = 2
andcount = 2
.
- Insert
-
enQueue(3):
- Insert
3
attail
index 2. - Set
tail = (2 + 1) % 3 = 0
(wrap-around) andcount = 3
.
- Insert
-
enQueue(4):
- The queue is full (
count == capacity
), so returnfalse
.
- The queue is full (
-
Rear():
- Calculate rear index as
(tail - 1 + capacity) % capacity = (0 - 1 + 3) % 3 = 2
. - Return element at index 2, which is
3
.
- Calculate rear index as
-
deQueue():
- Remove the element at
head
index 0. - Set
head = (0 + 1) % 3 = 1
andcount = 2
.
- Remove the element at
-
enQueue(4):
- Insert
4
attail
index 0. - Set
tail = (0 + 1) % 3 = 1
andcount = 3
.
- Insert
-
Rear():
- Now the rear index is
(tail - 1 + capacity) % capacity = (1 - 1 + 3) % 3 = 3 % 3 = 0
. - Return element at index 0, which is
4
.
- Now the rear index is
Code Implementation
Python Code
Java Code
Complexity Analysis
-
Time Complexity:
Each operation—enQueue
,deQueue
,Front
,Rear
,isEmpty
, andisFull
—runs in O(1) time because all operations involve a fixed number of steps. -
Space Complexity:
The implementation uses O(k) space for the array, where k is the capacity of the circular queue.
Step-by-Step Walkthrough and Visual Example
Imagine a circular queue of capacity 3:
-
Initialization:
queue = [_, _, _]
,head = 0
,tail = 0
,count = 0
.
-
After enQueue(1):
- Insert 1 at index 0.
queue = [1, _, _]
,head = 0
,tail = 1
,count = 1
.
-
After enQueue(2):
- Insert 2 at index 1.
queue = [1, 2, _]
,head = 0
,tail = 2
,count = 2
.
-
After enQueue(3):
- Insert 3 at index 2.
queue = [1, 2, 3]
,head = 0
,tail = 0
(wrap-around),count = 3
.
-
Attempt enQueue(4):
- Queue is full (
count == capacity
), so returnfalse
.
- Queue is full (
-
Front() and Rear():
Front()
returns element at index 0 →1
.Rear()
returns element at index(tail - 1 + capacity) % capacity = (0 - 1 + 3) % 3 = 2
→3
.
-
After deQueue():
- Remove element at index 0.
queue
remains[1, 2, 3]
buthead
becomes 1 andcount = 2
.
-
After enQueue(4):
- Insert 4 at index 0 (tail position).
queue = [4, 2, 3]
,tail
becomes 1,count = 3
.
-
Rear() now returns:
- Element at index
(tail - 1 + capacity) % capacity = (1 - 1 + 3) % 3 = 3 % 3 = 0
→4
.
- Element at index
Common Mistakes and Edge Cases
Common Mistakes
-
Confusing Empty and Full:
Not maintaining acount
or using a reserved slot can lead to ambiguous conditions. Make sure you can distinguish between the two states. -
Incorrect Pointer Movement:
Forgetting to use modulo arithmetic when incrementinghead
ortail
will cause index out-of-bound errors. -
Mismanaging the Rear Calculation:
Sincetail
points to the next insertion point, always adjust to find the last element when returningRear()
.
Edge Cases
-
Queue Capacity 1:
Test the behavior when there is only one slot. Ensure that bothenQueue
anddeQueue
work correctly. -
Multiple Wrap-Arounds:
Enqueue and dequeue several times to ensure that the circular nature (wrap-around) of the queue works as expected.
Alternative Variations and Related Problems
Variations
-
Dynamic Circular Queue:
Instead of a fixed capacity, design a circular queue that can expand dynamically. -
Deque (Double-Ended Queue):
Extend the design to support insertions and deletions at both the front and rear.
Related Problems for Further Practice
-
Design Stack Using Queues/Arrays:
Practice designing other data structures using basic arrays or queues. -
Implementing Circular Buffer:
Similar to circular queues, a circular buffer is used in system design for buffering data streams.
GET YOUR FREE
Coding Questions Catalog
