Circular Queue —
Modulo wraps the array into a ring.
A circular queue (ring buffer) reuses space at the front of the array once elements are dequeued — eliminating the wasted memory of a linear queue. Front and rear pointers advance using modulo arithmetic to wrap around. Watch the rear pointer circle back to the start on LearnBug and the ring structure becomes completely intuitive.
Why circular over linear?
A linear queue built on an array wastes space. After dequeuing elements from the front, those slots are empty but unreachable — the front pointer only moves right. A circular queue solves this by wrapping: when the rear reaches the end of the array, it loops back to index 0 if there's space at the front. The array acts as a ring — no space is permanently lost.
The modulo trick
Advancing a pointer in a circular queue uses modulo: rear = (rear + 1) % capacity. When rear reaches the last index and increments, modulo wraps it back to 0. On LearnBug you watch front and rear jump around the ring — the pointer movement that looks confusing in code becomes obvious in the circular layout.
Capacity=5 — enqueue, dequeue, watch rear wrap
Enqueue A, B, C — rear advances 0→1→2
front=0, rear=2. Three slots filled. size=3.
size = 3 / 5
Dequeue A and B — front advances 0→1→2
front=2, rear=2. Indices 0 and 1 are now free but unreachable in a linear queue. In circular queue they'll be reused.
size = 1 / 5 | Slots 0 and 1 are free
Enqueue D, E, F — rear wraps from 4 → 0 → 1
rear = (2+1)%5 = 3 (D), (3+1)%5 = 4 (E), (4+1)%5 = 0 (F). Rear wraps to index 0 — reusing freed space!
size = 4 / 5 | Rear has wrapped around ✓
Full queue: enqueue G — rear wraps to index 1
rear = (0+1)%5 = 1. Queue full: size=5. Next enqueue would be rejected.
size = 5 / 5 | Queue FULL — next enqueue fails
Circular queue implementation
class CircularQueue:
def __init__(self, capacity):
self.capacity = capacity
self.queue = [None] * capacity
self.front = 0
self.rear = -1
self.size = 0
def enqueue(self, val):
if self.is_full():
return False
self.rear = (self.rear + 1) % self.capacity # wrap
self.queue[self.rear] = val
self.size += 1
return True
def dequeue(self):
if self.is_empty():
return None
val = self.queue[self.front]
self.front = (self.front + 1) % self.capacity # wrap
self.size -= 1
return val
def is_full(self): return self.size == self.capacity
def is_empty(self): return self.size == 0
def peek(self): return self.queue[self.front] if not self.is_empty() else NoneTime & Space Complexity
Frequently asked questions
How do you distinguish a full queue from an empty queue when front == rear?
This is the classic circular queue ambiguity. Three solutions: (1) maintain a separate size counter — simplest and clearest; (2) leave one slot always empty so full is (rear + 1) % cap == front; (3) use a boolean flag. The size counter approach is used above and is the most interview-friendly.
Where are circular queues used in real systems?
Ring buffers appear everywhere in systems programming: audio/video streaming buffers, keyboard input buffers in OS kernels, network packet buffers, circular logs, and producer-consumer pipelines. They're ideal when you have a fixed-size buffer and need to process data at a steady rate without dynamic allocation.
What is the LeetCode problem for circular queue?
LeetCode 622 — Design Circular Queue. You implement the class with enQueue, deQueue, Front, Rear, isEmpty, and isFull methods. The follow-up LC 641 (Design Circular Deque) adds operations at both ends. Both test whether you understand modulo pointer arithmetic.
Why use modulo instead of if-else for wrapping?
(index + 1) % capacity handles wrapping in one expression regardless of the current index. An if-else (if rear == capacity-1: rear = 0 else: rear += 1) works too, but modulo is cleaner and less error-prone — one line instead of three, no branch to forget.
How does Python's collections.deque relate to circular queues?
Python's collections.deque is implemented internally as a doubly-linked list of fixed-size blocks — not a circular array. It gives O(1) append and popleft but without a fixed capacity. A circular queue (ring buffer) has fixed capacity and is array-based — better cache performance and no memory allocation overhead.
Watch front and rear wrap around the ring
Paste your circular queue operations into LearnBug and see every enqueue and dequeue with pointer positions.