Queues — Visual Learning

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.

O(1)Enqueue / Dequeue
O(n)Space
PythonLanguage

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.

YouTube — Circular Queue Explained
📺 Drop your YouTube embed here
LearnBug — front and rear wrapping around the ring
🖼 Add a LearnBug screenshot here
Walkthrough

Capacity=5 — enqueue, dequeue, watch rear wrap

1

Enqueue A, B, C — rear advances 0→1→2

front=0, rear=2. Three slots filled. size=3.

Afront=0
B
Crear=2
·
·

size = 3 / 5

2

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.

·freed
·freed
Cfront=rear=2
·
·

size = 1 / 5  |  Slots 0 and 1 are free

3

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!

Frear=0
·
Cfront=2
D
E

size = 4 / 5  |  Rear has wrapped around ✓

4

Full queue: enqueue G — rear wraps to index 1

rear = (0+1)%5 = 1. Queue full: size=5. Next enqueue would be rejected.

F0
Grear=1
Cfront=2
D
E

size = 5 / 5  |  Queue FULL — next enqueue fails

Python Code

Circular queue implementation

PythonCircularQueue — O(1) enqueue and dequeue
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 None

Time & Space Complexity

Enqueue / DequeueO(1)Just pointer arithmetic and one array write/read
SpaceO(n)Fixed capacity array — no dynamic resizing
vs Linear QueueNo wasted spaceLinear queue leaves dead slots at front; circular reuses them
Full vs EmptyUse size counterTrack size separately — front==rear is ambiguous without it

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.

Open Playground →