#622 Design Circular Queue
the problem
In plain words
Implement a fixed-capacity FIFO queue MyCircularQueue(k) with enQueue, deQueue (return true/false), Front, Rear (return -1 if empty), isEmpty and isFull, reusing the freed space at the front.
in k = 3; enQueue(1), enQueue(2), enQueue(3), enQueue(4), Rear(), isFull(), deQueue(), enQueue(4), Rear()
out true, true, true, false, 3, true, true, true, 4
The 4th enQueue fails (full). After one deQueue, 4 fits into the freed slot at index 0.
- 1 ≤ k ≤ 1000
- 0 ≤ value ≤ 1000
- At most 3000 calls
your turn
Try it before you peek
Read the examples, say the brute force out loud, then write code: here or on LeetCode ↗. Stuck for 20 minutes? Open one hint at a time.
Solve it in the browser
Design: implement every operation. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.
Solutions are hidden so you can try first. Even 20 minutes of real effort makes the answer stick far better than reading it.
Marking the problem solved, or opening every hint, also shows it.
the aha
Key insight
patternStack & queue design
Store head and size. The rear index is (head + size − 1) % k and the next free slot is (head + size) % k — no separate tail needed, and no empty/full ambiguity.
solutions
From brute force to optimal
Append to the end; dequeue with remove(0).
- enQueue: reject if size == k, else
add. - deQueue:
remove(0). - Front/Rear: first/last element or −1.
remove(0) shifts every remaining element left.
At most k elements stored.
// On LeetCode, name this class MyCircularQueue.class MyCircularQueueList { private final List<Integer> items = new ArrayList<>(); private final int capacity; public MyCircularQueueList(int k) { capacity = k; } public boolean enQueue(int value) { if (items.size() == capacity) return false; items.add(value); // amortized O(1) return true; } public boolean deQueue() { if (items.isEmpty()) return false; items.remove(0); // shifts every other element left: O(n) return true; } public int Front() { return items.isEmpty() ? -1 : items.get(0); } public int Rear() { return items.isEmpty() ? -1 : items.get(items.size() - 1); } public boolean isEmpty() { return items.isEmpty(); } public boolean isFull() { return items.size() == capacity; }}A ring buffer: indices wrap with % k; dequeue just moves head.
- enQueue: if full return false; write at
(head + size) % k; size++. - deQueue: if empty return false;
head = (head + 1) % k; size--. - Front:
data[head]; Rear:data[(head + size − 1) % k].
Each call does a constant number of arithmetic steps; nothing shifts.
One array of capacity k.
class MyCircularQueue { private final int[] data; private int head = 0; // index of the front element private int size = 0; // number of stored elements public MyCircularQueue(int k) { data = new int[k]; } public boolean enQueue(int value) { if (isFull()) return false; int tail = (head + size) % data.length; // first free slot, wrapping around data[tail] = value; size++; return true; } public boolean deQueue() { if (isEmpty()) return false; head = (head + 1) % data.length; // just move head; no shifting size--; return true; } public int Front() { return isEmpty() ? -1 : data[head]; } public int Rear() { return isEmpty() ? -1 : data[(head + size - 1) % data.length]; } public boolean isEmpty() { return size == 0; } public boolean isFull() { return size == data.length; }}Dry run
in the room
How to say it in the interview
talk track
- Start by naming the empty-vs-full problem with only head and tail — then fix it with a size counter.
- Write the two index formulas first; the methods are one-liners after that.
- Test aloud with k = 1 and with a wrap at the last index.
traps
- Rear computed as
data[tail − 1]when tail is 0 → index −1. - Not checking empty before Front/Rear.
what they ask next
Follow-ups
Make it thread-safe?
Guard methods with a lock, or use ArrayBlockingQueue, which is exactly a bounded circular array with locks and conditions.
Without a size field?
Keep one slot always empty: empty when head == tail, full when (tail + 1) % k == head (allocate k + 1 slots).
before you submit
Edge cases to test
- k = 1: the single slot is both front and rear; enQueue → deQueue → enQueue must reuse it.
- Front and Rear on an empty queue return −1 — check
sizebefore reading the array. - Wrap-around: after a few deQueues the next free slot is index 0, and Rear must read across the wrap.
practise next
Similar problems
- #641Design Circular DequeMediumin Queues
- #232Implement Queue using StacksEasyin Queues
- #362Design Hit CounterMediumin QueuesLeetCode Premium