What is a Circular Queue?
A circular queue takes a regular array-based queue and wraps its rear index back to the beginning once it hits the end: instead of a straight line, the underlying array is treated like a loop.
Key Characteristics
Circular queues have these fundamental properties:
- Fixed capacity: Size is predetermined at creation
- Two pointers:
- Front: Points to the first element
- Rear: Points to the last element
- Circular behavior: When pointers reach the end, they wrap around to the start
- Efficient space utilization: Reuses empty spaces created after dequeues
How Does It Work?
The array is the same as ever — only the arithmetic changes. Picture the five slots bent into a ring so index 4 hands straight over to index 0. front marks the first element, rear marks the next free slot, and both advance with (i + 1) % capacity:
- Start empty. front and rear both sit on index 0.
- enqueue(10), enqueue(20), enqueue(30) — each write lands on rear, then rear steps forward.
- dequeue() twice — 10 and 20 leave, and front steps forward to index 2. Slots 0 and 1 are now free again.
- enqueue(40), enqueue(50) — rear fills index 3, then 4, and wraps back around to index 0.
- enqueue(60) — it reuses slot 0, freed way back by the first dequeue. Now (rear + 1) % 5 === front, so the queue reports full.
Step 5 is what a linear array queue cannot do. There, once rear reached the end the queue was "full" even with two empty slots at the start, and the only fix was shifting every element down. Here rear simply wraps to index 0 and reuses that space in constant time.
It also shows why one slot is always sacrificed: front and rear landing on the same index has to mean something definite. Kept as "empty", a full queue must stop one slot short — which is why capacity 5 holds at most 4 elements.
Implementation Details
Key implementation aspects:
- Pointer Movement:
- front = (front + 1) % capacity
- rear = (rear + 1) % capacity
- Full/Empty Conditions:
- Full: (rear + 1) % capacity == front
- Empty: front == rear
- Always one empty slot:
- Needed to distinguish between full and empty states
Time Complexity
- enqueue(): O(1)
- dequeue(): O(1)
- peekFront(): O(1)
- peekRear(): O(1)
- isEmpty(): O(1)
- isFull(): O(1)
The modulo keeps every operation to a single index update, with no shifting and no scanning, so the cost never grows with the queue:
Applications
Circular queues are used in:
- CPU Scheduling: Round-robin scheduling algorithms
- Memory Management: Circular buffers in memory systems
- Traffic Systems: Controlling the flow of traffic signals
- Data Streams: Handling continuous data streams (audio/video buffers)
- Producer-Consumer Problems: Where producers and consumers operate at different rates
Advantages Over Linear Queue
- Better memory utilization: Reuses empty spaces
- Efficient operations: No need to shift elements
- Fixed memory footprint: Predictable memory usage
- Real-time systems friendly: Bounded execution time
That one change fixes the biggest annoyance with a plain array queue: slots freed up by earlier dequeues no longer go to waste. It keeps every operation running in constant time, which is why circular queues show up so often in fixed-size buffers, like the ones used in low-level or real-time systems.