Circular Queue
Table of Contents + −
A normal queue built on a plain array has one annoying problem. You keep removing items from the front. That front space just sits there empty. The array says it is full. But half of it is wasted. A circular queue fixes exactly this. So let us see how it reuses that empty space.
🎯 The Problem With a Simple Array Queue
Picture a queue made from an array of size 5. You add 5 people. Now it is full. Next you serve (remove) 2 people from the front. Those two slots at the start are empty now, right?
But here is the pain. In a simple queue, front moves forward and rear is stuck at the last index. When you try to add a new person, rear cannot move. It already points to the end of the array. So the queue reports that it is full even though two slots are clearly free.
That wasted space at the front is the whole problem. A circular queue is a queue where the rear wraps back to index 0 once it reaches the end. So the empty front slots get reused.
Think of it like a round dinner table. When you reach the last chair, the next chair is the first one again. There is no real “end” because it loops around.
Note
The array is still a straight line in memory. We only pretend it is a circle by doing clever index math. Nothing physically bends.
🔁 How Wrapping Works
The trick is one small formula. Instead of writing rear = rear + 1, we write rear = (rear + 1) % capacity.
The % is the modulo operator. It gives the remainder after division. So when rear is at the last index and we add 1, the remainder brings it back to 0.
Here is the same idea in plain points:
capacityis the total size of the array, say 5.- Normally
reargoes 0, 1, 2, 3, 4. - After 4,
(4 + 1) % 5is5 % 5which is0. Sorearjumps back to the front. - The same
% capacitytrick is used when movingfrontforward too.
That single % capacity is what makes the queue circular. Let us picture it.
See how index 4 points back to index 0? That arrow is the modulo formula in action.
🧮 Full and Empty: The Tricky Part
There is one catch with circular queues. How do you tell “completely empty” from “completely full”? In both cases front and rear can sit close together. So we keep a simple size counter. It tells us how many items are inside right now.
The two checks become easy:
- The queue is empty when
sizeis 0. - The queue is full when
sizeequalscapacity.
This counter approach is the cleanest way for beginners. No confusing index tricks needed.
📝 Steps to Build a Circular Queue
Before we jump into code, here is the plan. We keep an array, a front index, a rear index, and a size counter.
To enqueue (add an item at the rear):
- If
sizeequalscapacity, the queue is full. Stop and report it. - Move
rearforward with wrapping:rear = (rear + 1) % capacity. - Put the new value at the
rearposition. - Increase
sizeby 1.
To dequeue (remove an item from the front):
- If
sizeis 0, the queue is empty. Stop and report it. - Read the value at the
frontposition. - Move
frontforward with wrapping:front = (front + 1) % capacity. - Decrease
sizeby 1. - Return the value you read.
We start front at 0 and rear at -1 (or capacity - 1). So the very first enqueue lands rear at index 0.
💻 Circular Queue in 5 Languages
Now let us write it. The code below builds a small circular queue of capacity 5. It fills the queue. Then it removes a couple of items. Then it adds new ones so the rear wraps around. Finally it prints what is inside.
CAP = 5queue = [0] * CAPfront = 0rear = -1size = 0
# Add an item at the rear, wrapping if neededdef enqueue(value): global rear, size if size == CAP: print(f"Queue is full, cannot add {value}") return rear = (rear + 1) % CAP # wrap rear back to 0 at the end queue[rear] = value size += 1 print(f"Enqueued {value}")
# Remove an item from the front, wrapping if neededdef dequeue(): global front, size if size == 0: print("Queue is empty, nothing to remove") return value = queue[front] front = (front + 1) % CAP # wrap front back to 0 at the end size -= 1 print(f"Dequeued {value}")
# Print items from front to rear in orderdef display(): items = [str(queue[(front + i) % CAP]) for i in range(size)] print("Queue: " + " ".join(items))
enqueue(10)enqueue(20)enqueue(30)enqueue(40)enqueue(50)dequeue() # removes 10dequeue() # removes 20enqueue(60) # rear wraps to index 0enqueue(70) # rear wraps to index 1display()The output of the above code will be:
Enqueued 10Enqueued 20Enqueued 30Enqueued 40Enqueued 50Dequeued 10Dequeued 20Enqueued 60Enqueued 70Queue: 30 40 50 60 70Look at the last line of output. After removing 10 and 20, those slots at index 0 and 1 became free. Then 60 and 70 wrapped around. They landed in exactly those reused slots. That is the circular queue at work.
Tip
Both enqueue and dequeue run in O(1) time. There is no shifting of elements like a simple array queue might do. We just touch one index and update the counter. That is the big win.
⏱️ Time and Space Complexity
Here is how the circular queue operations stack up.
| Operation | Time | Space |
|---|---|---|
| Enqueue | O(1) | O(1) |
| Dequeue | O(1) | O(1) |
| Peek (front) | O(1) | O(1) |
| Whole queue storage | — | O(n) |
The whole queue needs O(n) space for n slots. Every single operation stays at O(1) because nothing ever shifts.
⚠️ Common Mistakes to Avoid
A few traps catch beginners with circular queues. Watch out for these:
- Forgetting the
% capacityand writing plainrear + 1. Thenrearruns off the end of the array and crashes. - Not keeping a
sizecounter and then mixing up empty with full. Both states can havefrontandrearin the same spot. - Forgetting to check “is it full?” before enqueue. You would overwrite a value that has not been served yet.
- Starting
rearat 0 instead of -1, so the first item lands in the wrong slot. Startrearat -1 with this counter style.
🧩 What You’ve Learned
✅ A simple array queue wastes the empty front slots and reports “full” too early.
✅ A circular queue reuses that space by wrapping the index with rear = (rear + 1) % capacity.
✅ A size counter is the clean way to tell an empty queue from a full one.
✅ Enqueue and dequeue both run in O(1) time because nothing ever shifts.
✅ Start front at 0 and rear at -1 so the first enqueue lands correctly.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What problem does a circular queue solve compared to a simple array queue?
Why: A circular queue wraps the rear back to the start, so the freed front slots get reused instead of being wasted.
- 2
Which formula correctly moves the rear index in a circular queue?
Why: The modulo with capacity makes the index wrap back to 0 once it passes the last slot.
- 3
Why do we keep a size counter in this circular queue?
Why: Front and rear can sit in the same spot when empty or full, so the size counter clears up which state it is.
- 4
What is the time complexity of enqueue and dequeue in a circular queue?
Why: Both touch just one index and update the counter, so they run in constant O(1) time with no shifting.
🚀 What’s Next?
Now that the circular queue makes sense, keep building your queue skills: