LINEAR DATA STRUCTURES / ALGORITHM BRIEF

Circular queue

A circular queue (ring buffer) stores a queue in a fixed-size array and wraps indexes around with modulo arithmetic.

BeginnerPhase 02 / Topic 5 of 8Mental modelComplexityEdge cases
01

Overview

A circular queue (ring buffer) stores a queue in a fixed-size array and wraps indexes around with modulo arithmetic. When the back reaches the end of the array, it continues at index 0, reusing slots freed by dequeues.

It gives O(1) enqueue and dequeue with no memory allocation after creation, which is why it is used in audio pipelines, network drivers, logging systems, and anywhere memory must be predictable.

A rotating sushi belt

The belt has a fixed number of plates. The chef places new dishes in the next empty slot and customers take dishes as they pass. When the belt wraps around, the same slots are reused.

02

When to use it

  • The maximum size is known in advance.
  • You need O(1) queue operations with no allocations (real-time or embedded systems).
  • You want to keep only the last k items: recent logs, moving averages.
  • Round-robin problems where positions wrap around (circular arrays).
03

Problem patterns it solves

Ring buffer design

Recognize it when: implement a fixed-capacity queue or deque.

  • 622. Design Circular Queue
  • 641. Design Circular Deque
Last k items

Recognize it when: moving average, recent history, fixed-length logs.

  • 346. Moving Average from Data Stream
  • 1352. Product of the Last K Numbers
Circular array indexing

Recognize it when: the array wraps around; use index % n or iterate 2n times.

  • 503. Next Greater Element II
  • 918. Maximum Sum Circular Subarray
  • 213. House Robber II
  • 2582. Pass the Pillow
Josephus and elimination games

Recognize it when: players in a circle, eliminate every kth.

  • 1823. Find the Winner of the Circular Game
  • 2073. Time Needed to Buy Tickets
04

Where it is used in real software

Audio and video streaming

Sound cards and media players use ring buffers so the producer (decoder) and consumer (speaker) can run at slightly different speeds without allocating memory.

Network interface cards

NIC drivers share ring buffers of packet descriptors with the hardware for receiving and sending packets.

Logging and tracing

Flight recorders and in-memory log buffers keep only the most recent N entries by overwriting the oldest.

High-performance messaging

The LMAX Disruptor, used in trading systems, is a lock-free ring buffer handling millions of events per second.

05

Key terms

Capacity
Fixed number of slots k.
front / head
Index of the oldest element.
count
Number of stored elements; distinguishes full from empty.
Wrap-around
next = (index + 1) % capacity.
rear
(front + count - 1) % capacity, the index of the newest element.
06

Operations using front and count

  1. 1
    Allocate

    data = new Array(k), front = 0, count = 0.

  2. 2
    enQueue(x)

    If count === k, it is full. Otherwise write data[(front + count) % k] = x and count++.

  3. 3
    deQueue()

    If count === 0, it is empty. Otherwise front = (front + 1) % k and count--.

  4. 4
    Front()

    data[front] when not empty.

  5. 5
    Rear()

    data[(front + count - 1) % k] when not empty.

Circular queue with capacity 4
Step 1 / 5
front
0
1
2
3

STEP 1Empty: front = 0, count = 0.

07

Operation trace (capacity 3)

front = 0, count = 0

Step 1 / 7
OperationSlot written / freedfrontcountResult
enQueue(1)write slot 001true
enQueue(2)write slot 102true
enQueue(3)write slot 203true
enQueue(4)-03false (full)
deQueue()free slot 012true
enQueue(4)write slot (1 + 2) % 3 = 013true
Rear()slot (1 + 3 - 1) % 3 = 0134

NOWOperation: enQueue(1) | Slot written / freed: write slot 0 | front: 0 | count: 1 | Result: true

Tracking count avoids the classic ambiguity where front === rear could mean either full or empty.

08

Implementation

class MyCircularQueue {  constructor(k) {    this.data = new Array(k);    this.capacity = k;    this.front = 0;    this.count = 0;  }  enQueue(value) {    if (this.isFull()) return false;    this.data[(this.front + this.count) % this.capacity] = value;    this.count++;    return true;  }  deQueue() {    if (this.isEmpty()) return false;    this.front = (this.front + 1) % this.capacity;    this.count--;    return true;  }  Front() { return this.isEmpty() ? -1 : this.data[this.front]; }  Rear() {    return this.isEmpty() ? -1 : this.data[(this.front + this.count - 1) % this.capacity];  }  isEmpty() { return this.count === 0; }  isFull() { return this.count === this.capacity; }} // Circular array trick: iterate 2n times with i % nfunction nextGreaterElements(nums) {  const n = nums.length;  const result = new Array(n).fill(-1);  const stack = [];  for (let i = 0; i < 2 * n; i++) {    const idx = i % n;    while (stack.length && nums[stack.at(-1)] < nums[idx]) {      result[stack.pop()] = nums[idx];    }    if (i < n) stack.push(idx);  }  return result;}
09

Complexity and performance

enQueue / deQueueO(1)

Index arithmetic only.

SpaceO(k)

Allocated once.

Allocations after creation0

Predictable memory and latency.

10

Trade-offs

Fixed capacity

You must reject, block, or overwrite when full. Choose the policy deliberately: logs overwrite, task queues reject or block.

Modulo cost

Modulo is slightly slower than a comparison; high-performance buffers use power-of-two capacities and index & (capacity - 1).

11

Variants and related techniques

Overwriting ring buffer

When full, advance front and overwrite the oldest element.

Circular deque

Also insert at the front with front = (front - 1 + k) % k.

Lock-free single-producer single-consumer

Separate read and write indexes updated atomically allow concurrent use without locks.

12

Common mistakes

  • Ambiguous full vs empty when using only front and rear.

    Fix: Track count, or leave one slot always empty.

  • Negative modulo in JavaScript and Java.

    Fix: (-1) % 4 is -1. Use (i - 1 + k) % k when moving backward.

  • Reading slots of dequeued items.

    Fix: Only read indexes within [front, front + count).

13

Interview questions

How do you tell full from empty in a ring buffer?

Keep a count, or keep one slot unused so full means (rear + 1) % k === front and empty means front === rear.

How do you handle circular arrays in problems like Next Greater Element II?

Iterate from 0 to 2n - 1 and use i % n, which simulates walking around the circle twice.

14

Practice problems

ProblemDifficultyWhat it trains
622. Design Circular QueueMediumfront + count design.
641. Design Circular DequeMediumBackward wrap-around.
346. Moving Average from Data StreamEasyFixed window buffer.
2582. Pass the PillowEasyCircular movement math.
503. Next Greater Element IIMedium2n iteration trick.
918. Maximum Sum Circular SubarrayMediumKadane on wrap-around.