🎉 All 30 days are live — the full DSA-30 course, from Big-O to System Design. See the roadmap →

Queue Variants

A plain queue serves strictly in arrival order. But what if “most important” should jump ahead of “first in line”? Or you need to grab work from both ends? Three small twists on the FIFO rule — circular, deque, priority — unlock scheduling, sliding-window tricks, and Dijkstra. Each is a tiny change with outsized power.

The plain FIFO queue is the starting point. Real codebases use a handful of variants that bend the rules in useful ways. The four worth knowing:

  1. Linear queue — the naive baseline (and its flaw).
  2. Circular queue — fixes the flaw with modular arithmetic.
  3. Deque — both ends are fair game.
  4. Priority queue — order by importance, not arrival time.

1. Linear queue — and why nobody uses it

The straightforward version: two indices, enqueue at rear, dequeue at front. We covered this in Basic Operations.

The problem: once front moves forward, the slots behind it are dead weight. After a few cycles, your queue reports “Overflow” while half the array is empty.

capacity 5, after enqueue×5 + dequeue×3:

[_, _, _, x, x]    front=3, rear=5
              ^ rear at the end

We can't enqueue another element — even though slots 0, 1, 2 are free.

Fixes:

  • Shift everything left on every dequeue — correct, but O(n) per dequeue. Brutal.
  • Use a circular array — keep the same memory, just wrap.

The second one is what everyone actually does.

2. Circular queue

The fix is one operator: %. When rear reaches the end of the array, it loops back to slot 0 — and since front has already moved past those slots, they’re free to reuse.

enqueue:  rear  = (rear  + 1) % capacity
dequeue:  front = (front + 1) % capacity

That’s the whole idea. Two pointers chasing each other around a ring.

Try it: Circular Queue
Watch how front and rear wrap around the ring with modular arithmetic.
[0][1][2][3][4][5]size0/6

Enqueue past the end and watch rear wrap to slot 0 — as long as front has moved on, those slots are yours again.

”Empty vs full” — the classic bug

In a circular buffer, both empty and full can look like front == rear. There are two standard fixes:

TrickCostVerdict
Keep an explicit count variableOne extra intSimple, recommended.
Leave one slot permanently unused”Full” = (rear + 1) % cap == front. Saves a field but loses a slot.Memory-tight cases only.

We used the count approach in the previous page. It’s a tiny price for never having to think about the bug.

Circular buffers show up far outside DSA class — they’re the standard structure for audio buffers, network packet rings, kernel ring buffers (dmesg), and lock-free producer-consumer queues. The pattern is everywhere.

3. Deque (Double-Ended Queue)

A deque (“deck”) relaxes the queue rule: you can add or remove from either end in O(1).

OperationWhat it does
pushFront(x)Add x to the front
pushBack(x)Add x to the back
popFront()Remove and return the front
popBack()Remove and return the back
peekFront()Look at the front
peekBack()Look at the back

A deque is a strict generalization — it can act as a stack or a queue. That’s why most modern standard libraries skip a dedicated queue type and just give you a deque (Python’s collections.deque, Java’s ArrayDeque, C++‘s std::deque).

Usage

When deques really shine

  • Sliding-window problemsSliding Window Maximum keeps a deque of indices in monotonic order, popping from both ends.
  • Undo + redo with bounded history — push new actions on the back, drop old ones from the front when the buffer fills.
  • Work-stealing schedulers — each thread takes work from one end of its deque; thieves take from the other.
⚠️

A deque is more powerful than a queue or stack, but that flexibility costs nothing. Defaulting to ArrayDeque / collections.deque is almost always the right call in interview code.

4. Priority Queue

A priority queue doesn’t care about insertion order. It hands you the highest-priority element next — regardless of when it arrived.

enqueue(task with priority 3)
enqueue(task with priority 1)   ← higher priority (lower number)
enqueue(task with priority 5)

dequeue → task with priority 1  ← jumps the queue
dequeue → task with priority 3
dequeue → task with priority 5

(Whether “higher priority” means lower or higher numbers is just convention. Min-heaps are most common in DSA problems.)

Predict first
Classic trick: to track the K LARGEST elements in a stream of millions, you keep a heap of size K. Should it be a min-heap or a max-heap?

The implementation: a heap

You could build a priority queue with a sorted list — but enqueue would be O(n). You could use an unsorted list — but dequeue would be O(n).

The standard answer is a binary heap, which gives:

OperationTime
Enqueue (push)O(log n)
Dequeue (pop)O(log n)
Peek topO(1)
Build from n elementsO(n)

We’ll build heaps from scratch on Day 7. For now, here’s the practical usage:

Where priority queues live

  • Dijkstra’s shortest path — always expand the closest unvisited node next.
  • A* search — same idea, with a heuristic.
  • Huffman coding — repeatedly merge the two smallest frequencies.
  • Job scheduling — run the most urgent task next.
  • Top-K problems — “find the K largest elements” → keep a min-heap of size K.
  • Event simulation — process events in chronological order across millions of events.

Variant cheat sheet

VariantEnqueueDequeueOrderUse it when…
LinearO(1)O(1)*FIFOAlmost never — see Circular.
CircularO(1)O(1)FIFOFixed-size buffer, lots of enqueue/dequeue churn.
DequeO(1)O(1)Both endsYou want stack+queue, or sliding-window tricks.
PriorityO(log n)O(log n)By priority”Next most important” beats “next oldest”.

* O(1) in time per operation, but burns array slots until you wrap to circular.

Quick check

Why do most modern standard libraries (Python deque, Java ArrayDeque) offer a deque instead of a dedicated queue type?

The priority queue you just met is really a heap wearing a queue’s API — and it’s so important it gets its own chapter. That completes Day 4. Next: Day 6 — Binary Trees & BST for hierarchy, and Day 7 — Heaps where you’ll build the priority queue from scratch. First, lock in stacks and queues with the practice problems.

Finished this page?