Queue
A first-in, first-out collection: elements enter at the back and leave from the front.
Definition
A queue supports enqueue (add at the back) and dequeue (remove from the front). Elements leave in the exact order they arrived — FIFO.
A naive array with shift() from the front costs O(n) per dequeue. Correct implementations use a Linked List with head and tail pointers, a Circular Queue over a fixed buffer, or two stacks. All achieve O(1) per operation.
Queues are the engine behind Breadth-First Search (BFS): they guarantee nodes are explored in order of discovery, which is what makes BFS find shortest paths in unweighted graphs.
Intuition
A mental model before the formal terms.
A line at a ticket counter. New people join at the back; the person at the front is served next. Nobody cuts in line, so service order equals arrival order.
A conveyor belt: items placed on one end come off the other end in the same sequence.
How it works
- Keep a linked list with
head(front) andtail(back) pointers, or an array withheadandtailindices. enqueue(x): create a node, link it aftertail, movetailto it (or set bothheadandtailif empty).dequeue(): returnheadvalue and advanceheadtohead.next; if the list becomes empty, resettailto null.peek(): return the value atheadwithout removing it.
Why it works
Maintaining explicit pointers to both ends means neither insertion nor removal has to traverse or shift anything, so each operation is a constant number of pointer updates.
FIFO order is exactly "process things in the order they were discovered", which is the invariant that makes level-order traversal and BFS shortest paths correct: all distance-d nodes are dequeued before any distance-d+1 node.
Operations
| Operation | Description | Cost |
|---|---|---|
| enqueue(x) | Add x at the back. | O(1) |
| dequeue() | Remove and return the front element. | O(1) |
| peek() / front() | Return the front element without removing it. | O(1) |
| isEmpty() | True when the queue is empty. | O(1) |
| size() | Number of stored elements. | O(1) |
Recognition
How to tell a problem wants this.
- The problem mentions level-by-level, shortest path in an unweighted graph, or minimum number of steps.
- Tasks must be processed in arrival order: schedulers, message buffers, rate limiters, print jobs.
- You are simulating a real queue: sliding time windows of recent events, "hit counter" designs.
Interactive demo
Play, step, change the input. ← → and space work too.
1enqueue(x): items.append(x) # at the back2dequeue(): return items.pop_front() # from the front3peek(): return items[front]Pseudocode
1class Queue:2 head = null, tail = null, n = 03 enqueue(x): node = Node(x); if tail: tail.next = node else head = node; tail = node; n++4 dequeue(): if empty: error; v = head.val; head = head.next; if head == null: tail = null; n--; return v5 peek(): return head.valImplementation
1from typing import Generic, Optional, TypeVar2 3T = TypeVar("T")4 5 61 · Node and end pointers7class _QNode(Generic[T]):8 __slots__ = ("value", "next")9 10 def __init__(self, value: T) -> None:11 self.value = value12 self.next: Optional[_QNode[T]] = None13 14 15class Queue(Generic[T]):16 def __init__(self) -> None:17 self._head: Optional[_QNode[T]] = None # front: dequeue here18 self._tail: Optional[_QNode[T]] = None # back: enqueue here19 self._n = 020 212 · Enqueue at the tail22 def enqueue(self, x: T) -> None:23 node = _QNode(x)24 if self._tail is not None:25 self._tail.next = node26 else:27 self._head = node # was empty: node is both ends28 self._tail = node29 self._n += 130 313 · Dequeue from the head32 def dequeue(self) -> T:33 if self._head is None:34 raise IndexError("dequeue from empty queue")35 value = self._head.value36 self._head = self._head.next37 if self._head is None:38 self._tail = None # emptied: drop the stale tail39 self._n -= 140 return value41 424 · Peek, size, emptiness43 def peek(self) -> T:44 if self._head is None:45 raise IndexError("peek from empty queue")46 return self._head.value47 48 def is_empty(self) -> bool:49 return self._n == 050 51 def __len__(self) -> int:52 return self._n_QNodeuses__slots__to keep per-node memory down; the leading underscore marks it internal.enqueuelinks after_tailor initialises both pointers when empty.dequeueadvances_headand clears_tailwhen the queue empties; the removed node is garbage-collected.__len__lets callers writelen(q)andwhile q:naturally.
collections.deque does the same job in C with block allocation — use it in real code; list.pop(0) is O(n).
collections.dequeis the canonical Python queue:append()to enqueue,popleft()to dequeue, both O(1).queue.Queueis for *threads* — it adds locking you do not want in single-threaded algorithm code.- This class is didactic; in interviews, saying "I would use deque" and using it is the expected move.
- Using
list.pop(0)— every remaining element shifts left, O(n) per dequeue. - Reaching for
queue.Queuein an algorithm and paying lock overhead for nothing. - Forgetting to clear
_tailon the last dequeue, so the nextenqueueappends to a detached node.
- Built-ins: C++
std::queueand Pythoncollections.dequeare ready-made O(1) queues; JS/TS have none — a naiveArray.shift()dequeue is O(n). - Python
queue.Queueis a thread-synchronised channel, not an algorithm queue; the JS/TS equivalent trap isshift()in hot loops. - Memory management: the C++ version must delete nodes (destructor) and forbid shallow copies; GC languages only need the pointer logic.
- C++
std::queue::pop()returnsvoid; JS/TS/Python dequeues return the removed value.
Complexity
| Operation | Average | Worst | Note |
|---|---|---|---|
| Access | O(n) | O(n) | Front is O(1). |
| Search | O(n) | O(n) | |
| Insert | O(1) | O(1) | Only at the back. |
| Delete | O(1) | O(1) | Only at the front. |
| Update | — | — | |
| Enqueue | O(1) | O(1) | |
| Dequeue | O(1) | O(1) | |
| Peek | O(1) | O(1) | |
| Space | O(n) | ||
Advantages & disadvantages
- Constant-time insert and remove at opposite ends with the right backing store.
- Preserves arrival order, giving fairness in scheduling and correctness in BFS.
- Simple to reason about; bounded versions naturally provide back-pressure.
- No random access or search better than
O(n). - Naive array implementations (
shift/pop(0)) degrade toO(n)per dequeue. - Linked-list versions pay per-node allocation and poorer cache locality than arrays.
Use cases
- Breadth-first search and level-order tree traversal.
- Task scheduling, job queues, producer/consumer pipelines.
- Buffering I/O and network packets.
- Simulations of waiting lines and time-ordered event processing.
- Order of processing must equal order of arrival.
- Breadth-first exploration: shortest path in unweighted graphs, level-order traversal, multi-source spread (rotting oranges).
- Decoupling producers from consumers with a buffer.
- You need to process the most recent item first — use a Stack.
- Items have priorities and the highest priority must leave first — use a Priority Queue.
- You need efficient push/pop at both ends — use a Deque.
Alternatives
Common mistakes
- Using
list.pop(0)in Python orArray.shift()in JS for a hot loop — both areO(n). Usecollections.dequeor a head-index. - Forgetting to reset
tailwhen the last element is dequeued, leaving a dangling pointer that corrupts the next enqueue. - In BFS, marking a node visited when it is dequeued instead of when it is enqueued, causing duplicates in the queue.
- Iterating a level in BFS without first capturing
len(queue), so newly enqueued children are mistaken for the current level.
Interview patterns
- BFS level-order traversal: snapshot queue size, process exactly that many nodes per level.
- Implement a queue with two stacks: push to
in, pop fromout, refilloutby draininginwhen empty (amortized O(1)). - Multi-source BFS: enqueue all sources at distance 0 before starting.
- Sliding time-window counters: dequeue timestamps older than the window.
- Course ScheduleIntermediate
- Network Delay TimeAdvanced
- Number of IslandsIntermediate