First-in-first-out processing: deque mechanics, circular buffers, queue-with-stacks, priority-queue preview, and why BFS is a queue wearing a trench coat.
0 of 7 lessons checked off
Introduction
What it is
A queue admits elements at the rear and releases them from the front — First In, First Out (FIFO). enqueue, dequeue, front, rear, is_empty, size are the whole interface.
Cousins you'll meet immediately: the deque (both ends open), the circular queue (fixed buffer, wrapping indexes), and the priority queue (position by priority, not arrival — a heap in practice).
Why it matters
Queues encode fairness and order preservation: process things in the order they arrived. That is precisely what breadth-first search needs — explore all distance-k nodes before any distance-k+1 node.
The implementation detail interviews test: a Python list makes a BAD queue (pop(0) is O(n)); collections.deque is the O(1) tool.
How it works
Linked-list backing: keep head and tail pointers; enqueue at tail, dequeue at head, both O(1).
Array backing must avoid shifting — the circular trick: front and rear indexes advance modulo capacity, reusing freed slots.
Where it's used
Print spoolers, message brokers (Kafka, SQS), CPU run queues, rate limiters, and keyboard input buffers — anywhere producers and consumers run at different speeds.
In interviews
BFS on trees/graphs/grids (level order, shortest unweighted path), design circular queue, implement queue with stacks, sliding-window maximum (monotonic deque), recent-calls counter.
Analogy: A queue is the line at a coffee shop: you join at the back, you're served at the front, and anyone 'optimising' that order loses friends. The circular queue is the same line painted around a circular counter.
Interactive diagram
FIFO in action — arrival order is exactly service order.
empty
An empty queue
A queue exposes both ends: items enter at the rear (enqueue) and leave from the front (dequeue). First in, first out.
1 / 6
Lessons in this topic
Check off lessons as you go — your progress is saved in this browser.
Queue fundamentals and FIFO
The contract, both ends, and the six operations.
15 min
Array-based and circular queues
Why naive arrays fail, and how modulo indexes fix it.
25 min
Linked-list-based queue
Head+tail pointers give O(1) at both ends.
15 min
Deque (double-ended queue)
collections.deque: O(1) at both ends, the Swiss-army lineal structure.
15 min
Queue using stacks
Two stacks, amortised O(1) — a favourite warm-up design question.
20 min
Priority queue (preview)
Serve by priority instead of arrival; implemented as a heap — full topic later.
15 min
BFS use cases
Level-order traversal and shortest paths in unweighted graphs.
25 min
Operations
Enqueue, dequeue, front, rear
Two ends, two pointers, no traversal: every queue operation is O(1) with the right backing.
empty
An empty queue
A queue exposes both ends: items enter at the rear (enqueue) and leave from the front (dequeue). First in, first out.
1 / 6
1fromcollectionsimportdeque234classQueue:5"""FIFO queue over collections.deque. All operations O(1)."""67def__init__(self)->None:8self._items:deque[str]=deque()910defenqueue(self,value:str)->None:11self._items.append(value)# rear1213defdequeue(self)->str:14ifnotself._items:15raiseIndexError("dequeue from empty queue")16returnself._items.popleft()# front — O(1), unlike list.pop(0)1718deffront(self)->str:19ifnotself._items:20raiseIndexError("front of empty queue")21returnself._items[0]2223defrear(self)->str:24returnself._items[-1]2526defis_empty(self)->bool:27returnnotself._items2829def__len__(self)->int:30returnlen(self._items)
Time: O(1) per operationSpace: O(n)
Edge cases
dequeue/front on empty must raise.
One element: front and rear are the same value.
Interleaved enqueue/dequeue must preserve arrival order exactly.
Common mistakes
list.pop(0) — O(n) per dequeue, O(n²) across a BFS.
Using the deque's wrong end (appendleft with popleft turns it into a stack).
Circular queue on a fixed buffer
front and rear walk around a fixed array with modulo arithmetic; freed slots get reused and nothing ever shifts.
0
1
2
3
4
buf
·
·
·
·
·
Fixed buffer of size 5
A circular queue reuses a fixed array. Two indexes — front and rear — walk around it with modulo arithmetic instead of shifting elements.
1 / 5
1classCircularQueue:2"""Fixed-capacity FIFO with wrapping indexes. O(1) ops, zero shifting."""34def__init__(self,capacity:int)->None:5ifcapacity<=0:6raiseValueError("capacity must be positive")7self._buf:list[object|None]=[None]*capacity8self._front=09self._size=01011defenqueue(self,value:object)->None:12ifself.is_full():13raiseOverflowError("queue is full")14rear=(self._front+self._size)%len(self._buf)15self._buf[rear]=value16self._size+=11718defdequeue(self)->object:19ifself.is_empty():20raiseIndexError("dequeue from empty queue")21value=self._buf[self._front]22self._buf[self._front]=None23self._front=(self._front+1)%len(self._buf)24self._size-=125returnvalue2627defis_empty(self)->bool:28returnself._size==02930defis_full(self)->bool:31returnself._size==len(self._buf)
Time: O(1) per operationSpace: O(capacity), fixed up front
Edge cases
Full-vs-empty ambiguity when using only front/rear indexes — solved here by tracking size.
Wrap-around: rear = (front + size) % capacity.
Enqueue on full: raise or overwrite-oldest — a design decision to state (ring buffers overwrite).
Common mistakes
Computing rear without the modulo, walking off the buffer.
Using front == rear to mean empty AND full simultaneously without a size or a sacrificial slot.
Queue using two stacks
Pushes land in an in-box; dequeues come from an out-box, refilled by flipping the in-box when empty. Each element moves at most twice → amortised O(1).
Queue using two stacks
1classQueueWithStacks:2"""FIFO from two LIFOs. Amortised O(1) per operation."""34def__init__(self)->None:5self._in:list[int]=[]6self._out:list[int]=[]78defenqueue(self,value:int)->None:9self._in.append(value)1011defdequeue(self)->int:12ifnotself._out:# refill only when empty13whileself._in:14self._out.append(self._in.pop())# flip reverses order15ifnotself._out:16raiseIndexError("dequeue from empty queue")17returnself._out.pop()
Time: Amortised O(1) — each element is pushed/popped at most twiceSpace: O(n)
Edge cases
Dequeue with both stacks empty must raise.
Never flip while _out still has items — that would scramble order.
front() is _out[-1] if _out else _in[0].
Common mistakes
Flipping on every dequeue (O(n) each) instead of only when the out-box empties.
Flipping into the out-box while it's non-empty, interleaving generations.
Complexity analysis
Operation
Best
Average
Worst
Space
enqueue
O(1)
O(1)
O(1)
—
dequeue (deque/linked/circular)
O(1)
O(1)
O(1)
—
dequeue (Python list.pop(0))
O(n)
O(n)
O(n)
—
front / rear / is_empty
O(1)
O(1)
O(1)
—
search
O(1)
O(n)
O(n)
O(1)
One row is a trap on purpose: the list-backed dequeue. Quoting O(1) queue costs while using list.pop(0) is a classic interview own-goal.
Python implementation
Production-quality code with type hints, validation, and docstrings.
Linked-list queue with head and tail pointers
1fromtypingimportOptional234class_Node:5__slots__=("value","next")67def__init__(self,value:object):8self.value=value9self.next:Optional["_Node"]=None101112classLinkedQueue:13"""FIFO queue: dequeue at head, enqueue at tail. O(1) worst case."""1415def__init__(self)->None:16self._head:Optional[_Node]=None# front17self._tail:Optional[_Node]=None# rear18self._size=0
What interviewers expect you to know
What interviewers expect you to know
FIFO contract and why BFS requires it: the queue IS the level-order guarantee.
list.pop(0) is O(n); collections.deque.popleft() is O(1) — say it before they ask.
Circular queue mechanics: modulo indexes, the full/empty ambiguity, and one resolution (size counter or sacrificial slot).
Deque vs queue vs priority queue: both-ends vs one-way vs by-priority.
Classic follow-ups
"Implement a stack using queues" — the mirror exercise: rotate the queue after each push (O(n) push, O(1) pop).
"Why is your queue-with-stacks O(1) if the flip is O(n)?" — amortised: each element crosses at most twice; charge the flip to the elements moved.
"Sliding-window maximum?" — a monotonic deque: front holds the max, back evicts dominated values. Bridges to the monotonic pattern page.
How to explain a queue solution
For BFS, narrate the invariant: 'everything in the queue is at distance d or d+1, in order' — that sentence is the proof of shortest paths.
Name the backing structure and its costs when you write it: 'deque, so both ends are O(1).'
Common mistakes
list as a queue
pop(0) shifts every element: an innocent-looking BFS becomes O(V²). deque.popleft() exists for exactly this.
Marking visited at dequeue time
In BFS, mark nodes visited when ENQUEUED. Marking at dequeue lets the same node enter the queue many times — exponential blowup on dense graphs.
Circular full/empty confusion
front == rear is ambiguous. Track size explicitly or keep one slot empty; say which convention you chose.
Tail pointer left dangling
Dequeuing the last node must also null the tail; forgetting it makes the next enqueue append to a ghost node.
Flipping stacks too eagerly
In queue-with-stacks, flip only when the out-box is empty. Early flips both break order and destroy the amortised bound.
Practice problems
Hand-picked from the 355-problem DSA Atlas. Reveal hints and solutions progressively; mark and bookmark as you solve.
Commonly associated with: Amazon, Google, Microsoft
O(rows x cols) time · O(cols) space
Topic quiz
5 questions across concepts, code output, complexity, and scenarios. Answer all, then submit for explanations.
Frequently asked questions
When do I need a deque instead of a queue?
When both ends are active: sliding-window maximum (evict from the back, read from the front), palindrome checks, work-stealing schedulers. If only rear-in/front-out happen, a queue interface keeps intent clearer.
What is a priority queue relative to this page?
A queue where dequeue returns the highest-priority item, not the oldest. It's implemented with a binary heap — covered fully on the Heaps page. Interview shorthand: 'queue' = FIFO, 'priority queue' = heap.
Is a circular queue still relevant with dynamic arrays around?
Yes — fixed memory plus O(1) ops make ring buffers the standard in embedded systems, audio pipelines, and network cards, and 'Design Circular Queue' remains a common interview design question.
Summary & cheat sheet
Key takeaways
Queue = FIFO; deque.popleft(), never list.pop(0).
Circular queue: modulo indexes over a fixed buffer; resolve full-vs-empty explicitly.
Queue-with-stacks: flip lazily; amortised O(1).
BFS is queue-order made algorithmic: first arrival = shortest unweighted path.
Mark visited on enqueue, not dequeue.
Formulas & cheat sheet
rear index = (front + size) % capacity
Items in a circular buffer: (rear − front + capacity) % capacity (when tracking indexes only)
Amortised bound: total element moves ≤ 2n over n operations
Interview checklist
I can implement a circular queue with a size counter.
I can write BFS with a deque and enqueue-time visited marking.
I can implement a queue from two stacks and defend its amortised cost.
I know when a deque or priority queue is the actual requirement.