Data structures · 12 / 19
Queue visualization
A queue adds elements at the back and removes them from the front, so the first element added is the first one removed.
Time: O(1) in every case. Space: O(n). What Big-O notation means.
Last updated
About this lesson
- Concept it teaches
- First in, first out; enqueue, dequeue and the front
- Canonical source
- Knuth, TAOCP vol. 1, section 2.2.1 (stacks, queues and deques)
- Builds on
- Nothing, start here
- Concept it teaches
- First in, first out; enqueue, dequeue and the front
- Canonical source
- Knuth, TAOCP vol. 1, section 2.2.1 (stacks, queues and deques)
- Builds on
- Nothing, start here
I already know this: go to the check
See
+10 XP
Watch the real code run, one step at a time. Change the input and see what happens.
Predict first. Before you press Play, guess which element will come out first.
Run Queue
- Size
- 0
- Next out
- none
- Step
- 1/35
- Current
- Ruled out
Queue. It is empty. Waiting: 4, 8, 1, 6, 3, 9. Out: empty.
Outside the queue: back = -1
Add the numbers one at a time and remove one after every second addition. Then peek, remove everything that is left, and remove once more from the empty queue.
Add the numbers one at a time and remove one after every second addition. Then peek, remove everything that is left, and remove once more from the empty queue.
Step 1/35empty checkwrite
With focus on the player: Space play · ← → step · Home End jump
Understand
+15 XP
The idea in plain words: why it works, what it costs, when to use it. One question at the end.
A queue is a collection that adds elements at the back and removes them from the front. The element added first is the first one removed, which is why it is called first in, first out (FIFO). It works like a line at a shop: you join at the end and the person who has waited longest is served first.
How it works
A queue has three operations. The code here keeps the elements in an array and a number head, the index of the oldest element.
enqueue(x)putsxafter the last element, at the back.dequeue()returns the element atheadand movesheadone slot to the right.peek()returns the element atheadand leaves it in place.
When dequeue runs, nothing is shifted. The slot it leaves behind is shown hatched, and the front simply steps over it. That is why dequeue stays fast.
Time and space complexity
| Best case time | O(1) |
|---|---|
| Average case time | O(1) |
| Worst case time | O(1) |
| Extra space | O(n) |
- Time, O(1) for enqueue, dequeue and peek: each one writes or reads one slot, or moves one number. None of them depends on how many elements are waiting.
- Space, O(n): the queue stores its
nwaiting elements. This simple version also keeps the hatched slots, so a long-running queue wastes room. Real implementations reuse them in a circular buffer, where the end of the array wraps around to its start.
A worked example
Add 4, 8, 1, 6 and 3 in that order, with one removal after every second addition, as the lesson's run does. Then peek, remove everything that is left, and remove once more from the empty queue. The table lists the waiting elements from the front, and the value of head after each operation.
| Operation | Waiting, front first | head | Returns |
|---|---|---|---|
| enqueue(4) | 4 | 0 | |
| enqueue(8) | 4, 8 | 0 | |
| dequeue() | 8 | 1 | 4 |
| enqueue(1) | 8, 1 | 1 | |
| enqueue(6) | 8, 1, 6 | 1 | |
| dequeue() | 1, 6 | 2 | 8 |
| enqueue(3) | 1, 6, 3 | 2 | |
| peek() | 1, 6, 3 | 2 | 1 |
| dequeue() | 6, 3 | 3 | 1 |
| dequeue() | 3 | 4 | 6 |
| dequeue() | empty | 5 | 3 |
| dequeue() | empty | 5 | undefined |
The values went in as 4, 8, 1, 6, 3 and came out in the same order, 4, 8, 1, 6, 3. That is the whole guarantee of a queue. Notice that the array never lost a slot: after the last dequeue it still holds all five values, and head is 5, one past the last slot. The run counts 10 writes (five enqueues and five moves of head) and 6 comparisons, one emptiness check for each dequeue.
Pseudocode
Q.items is the array, Q.head is the index of the oldest element, and NIL stands for the undefined that the code returns.
ENQUEUE(Q, x)
Q.items[Q.items.length] = x
DEQUEUE(Q)
if Q.head == Q.items.length
return NIL // underflow
x = Q.items[Q.head]
Q.head = Q.head + 1
return x
PEEK(Q)
if Q.head == Q.items.length
return NIL
return Q.items[Q.head]
The queue is empty exactly when head has reached the end of the array. A circular buffer wraps both the head index and the index where new elements go with mod capacity, so the freed slots are reused.
When to use it, and when not to
Use a queue whenever things must be handled in the order they arrived:
- Waiting lines: print jobs, support tickets and requests to a server.
- Buffers: data that is produced faster than it is consumed.
- Breadth-first search: the queue holds the nodes that were found first, so the search moves outward one level at a time.
Do not use it when the newest item must go first. That is a stack. If the item with the highest priority must go first, whatever its arrival time, use a priority queue, as in Dijkstra's algorithm.
Where it is used
- Task and job queues. A print spooler or a server's list of waiting requests serves jobs in the order they arrived. That is fair: a later job can never pass an earlier one. The producer adds at the back, the workers take from the front, and the two do not have to run at the same speed.
- Breadth-first search. The search enqueues the unvisited neighbors of a node and then dequeues the next node to expand. First in, first out means every node at distance 1 leaves the queue before any node at distance 2, so the first time the search reaches a node it has used the fewest possible edges.
- Buffers and scheduling. Keyboard input and network packets arrive in bursts. A fixed-size circular buffer holds them until the program reads them in order. Round-robin CPU scheduling also uses a queue: take the process at the front, run it for a short time slice, and put it at the back.
Common mistake and edge cases
Variations and related ideas
A circular buffer reuses the slots that dequeue leaves behind. A deque allows adding and removing at both ends. A queue can also be built on a linked list that keeps a pointer to its last node, which makes both ends O(1), or from two stacks, where one takes the new elements and the other hands them out in reverse. A priority queue drops the arrival order and always returns the smallest item first; the binary heap implements it, and Dijkstra's algorithm depends on it. Breadth-first search is the standard use of a plain queue.
One question to finish
You enqueue 1, 2 and 3 into an empty queue, then dequeue twice. Which value is at the front now?
Predict
+20 XP
The run stops and asks what happens next. The real run says if you were right.
Practice
+30 XP
Order the lines, fill in the gap, trace it by hand. The real run corrects every answer.
Check yourself
Four questions from memory. They also join your daily review. Questions return on a spaced schedule: right answers come back later, misses come back tomorrow.