Queues
First In, First Out (FIFO)
CS205 Data Structures
Arrow keys to navigate
What is a Queue?
A queue is a collection with First-In, First-Out (FIFO) access.
- Elements added at the rear (back)
- Elements removed from the front
- No access to elements in the middle
Analogy: Coffee Shop Line
First person in line is served first. New arrivals join the back. No cutting!
Key Idea
FIFO = the element waiting the longest gets served first. Fair and orderly!
The Queue ADT
Core Operations
| Operation | Description | Time |
enqueue(e) | Insert at rear | O(1) |
dequeue() | Remove & return front | O(1) |
front() | Return front (no remove) | O(1) |
isEmpty() | Is the queue empty? | O(1) |
size() | Number of elements | O(1) |
Key Idea
All core operations are O(1) — constant time regardless of queue size.
Java Interface
public interface Queue<E> {
/** Insert element at the rear. */
void enqueue(E element);
/** Remove and return front element. */
E dequeue();
/** Return front without removing. */
E front();
int size();
boolean isEmpty();
}
Enqueue & Dequeue
Add at the rear, remove from the front — synced with code
Enqueue Code
public void enqueue(E element) {
if (size == data.length)
throw new IllegalStateException();
data[rear] = element;
rear = (rear + 1) % data.length;
size++;
}
Dequeue Code
public E dequeue() {
if (isEmpty()) throw ...;
E result = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
return result;
}
Array Visualization
Stack vs Queue
Stack — LIFO
Last In, First Out
| Insert | push(e) |
| Remove | pop() |
| Peek | top() |
Queue — FIFO
First In, First Out
| Insert | enqueue(e) |
| Remove | dequeue() |
| Peek | front() |
Key Difference
Both are restricted-access. Stack: insert & remove from same end. Queue: insert & remove from opposite ends.
Array-Based Queue (Naive Approach)
Why simple arrays fail for queues
The Problem: Dequeue Shifts O(n)
If we dequeue from index 0, we must shift everything left.
O(n) Dequeue!
Every dequeue shifts all remaining elements one position left. For n elements, that's O(n) work per dequeue.
Solution: don't shift — just move the front pointer. But then we waste space... unless we wrap around!
Array Visualization
Circular Array Implementation
Wrap around using modulo — no wasted space, no shifting!
The Magic Formula
Advance front: f = (f + 1) % N
Advance rear: r = (r + 1) % N
Modulo Wraps Around
If N = 8 and rear = 7:
(7 + 1) % 8 = 0
The index wraps back to the beginning! Array treated as a ring.
Circular Ring View
Circular Array: Step-by-Step Trace
Trace operations on capacity N=5 — watch front & rear wrap around
Circular Array: Full vs Empty
When front == rear, is it empty or full?
The Ambiguity
EMPTY: front == rear == 0
Solution 1: Size Counter
// Maintain a separate size field
isEmpty(): return size == 0
isFull(): return size == N
// Cleanest approach!
Solution 2: Waste One Slot
// Max capacity = N - 1
isFull(): return (rear+1) % N == front
// One slot always empty
Classic Bug
Without either solution, you cannot distinguish full from empty. Both have front == rear.
Linked-List-Based Queue
Head for dequeue, tail for enqueue — both O(1)
Enqueue (add at tail)
public void enqueue(E element) {
Node newest = new Node(element);
if (isEmpty()) head = newest;
else tail.next = newest;
tail = newest;
size++;
}
Dequeue (remove from head)
public E dequeue() {
E result = head.data;
head = head.next;
size--;
if (isEmpty()) tail = null;
return result;
}
Node Diagram
Why head AND tail?
head for O(1) dequeue. tail for O(1) enqueue. Without tail, enqueue traverses the whole list — O(n)!
Deque (Double-Ended Queue)
Pronounced "deck" — insert & remove at both ends
Operations
| Operation | Description |
addFirst(e) | Insert at front |
addLast(e) | Insert at rear |
removeFirst() | Remove from front |
removeLast() | Remove from rear |
Deque Subsumes Both
- Stack = addFirst + removeFirst
- Queue = addLast + removeFirst
Java: ArrayDeque and LinkedList both implement Deque.
Interactive Deque
Application: Print Job Queue
A shared printer processes jobs fairly — FIFO
Submit Print Jobs
Why a Queue?
Fairness: first come, first served. No starvation — every job eventually prints.
Printer Queue
Application: BFS (Breadth-First Search)
A queue drives level-by-level traversal
BFS Algorithm
queue.enqueue(start);
while (!queue.isEmpty()) {
node = queue.dequeue();
visit(node);
for (child : node.children)
queue.enqueue(child);
}
Queue: []
Visited:
Tree
Application: Hot Potato / Josephus Problem
Pass the potato k times, eliminate the holder. Last one wins!
Algorithm
while (queue.size() > 1) {
for (i = 1 to k)
queue.enqueue(queue.dequeue());
eliminated = queue.dequeue();
}
return queue.dequeue(); // winner
Players Circle
Priority Queue Preview
What if some elements are more urgent?
Regular Queue (FIFO)
Arrival order determines service.
Priority Queue
Priority determines service — NOT FIFO.
Key Idea
A Priority Queue is NOT a queue. The element with the highest priority (often minimum key) is removed first, regardless of arrival order.
Real-World Examples
- Emergency Room — critical patients first
- OS Scheduling — high-priority processes preempt others
- Dijkstra's Algorithm — expand nearest unvisited node
Analogy
Regular queue = deli counter with numbered tickets.
Priority queue = ER triage: sickest patient goes first, even if they just walked in.
Coming up: heaps — the efficient way to implement priority queues!
Common Pitfalls
Mistakes to avoid when working with queues
1. Forgetting Wrap-Around
Writing rear++ instead of rear = (rear+1) % N → ArrayIndexOutOfBounds!
// WRONG
rear++;
// CORRECT
rear = (rear + 1) % data.length;
2. Dequeuing When Empty
Always check isEmpty() before dequeue().
// WRONG
E item = queue.dequeue();
// CORRECT
if (!queue.isEmpty())
E item = queue.dequeue();
3. Confusing Front & Rear
Enqueue → rear. Dequeue → front. Mixing them up gives stack-like behavior.
4. Full vs Empty Confusion
Without a size counter, front==rear is ambiguous. Always use a count or waste-one-slot.
5. Forgetting to Null
After dequeue, set data[front] = null to avoid memory leaks (loitering references prevent GC).
E result = data[front];
data[front] = null; // help GC!
Summary & Cheat Sheet
Queue at a Glance
| Property | Value |
| Access Policy | FIFO |
enqueue | O(1) |
dequeue | O(1) |
front / peek | O(1) |
isEmpty / size | O(1) |
Implementations
| Circular Array | Linked List |
| enqueue | O(1) amortized | O(1) |
| dequeue | O(1) | O(1) |
| Memory | Compact, cache-friendly | Pointer overhead |
| Resize | O(n) when full | Never needed |
Key Formulas (Circular Array)
Advance front: f = (f + 1) % N
Advance rear: r = (r + 1) % N
Is empty: size == 0
Is full: size == N
Queue Family
Applications
- BFS — level-order traversal
- Print spooling — fair job scheduling
- Hot Potato — circular elimination
- Buffering — producer-consumer
- OS scheduling — round-robin CPU
Challenge A: Circular Array Trace
Capacity N=4, starting with front=0, rear=0. After: enqueue(A), enqueue(B), dequeue(), enqueue(C), enqueue(D), enqueue(E) — what are front and rear?
Work it out:
- enqueue(A): data[0]=A, rear=1, size=1
- enqueue(B): data[1]=B, rear=2, size=2
- dequeue()→A: front=1, size=1
- enqueue(C): data[2]=C, rear=3, size=2
- enqueue(D): data[3]=D, rear=?, size=3
- enqueue(E): data[?]=E, rear=?, size=4
Challenge B: Fix the Bug
This linked-list dequeue has a bug. Find it!
Buggy Code
public E dequeue() {
if (isEmpty())
throw new NoSuchElementException();
E result = head.data;
head = head.next;
size--;
return result;
}
Bug: after dequeuing the last element, tail still points to the removed node. Next enqueue appends to a ghost!
What's missing?
The Fix
E result = head.data;
head = head.next;
size--;
if (isEmpty()) tail = null;
return result;
Why It Matters
Without this, tail references a removed node. The next enqueue does tail.next = newest — but tail is garbage! The new node is lost.
Think About It
The code correctly advances head and decrements size. But what about tail? When does tail need updating during a dequeue?
Challenge C: FIFO vs LIFO
You enqueue 1, 2, 3, 4, 5 into a queue, then dequeue all. What's the output?
Operations:
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
queue.enqueue(5);
while (!queue.isEmpty())
print(queue.dequeue());
Think: FIFO
First In, First Out. Which element was enqueued first? That one comes out first when you dequeue.
Bonus: If this were a stack instead, the output would be the reverse: 5, 4, 3, 2, 1 (LIFO).
Quiz: Queue Fundamentals
3 quick questions
Q1: What does FIFO stand for?
Q2: In a circular array of size 8, if rear=7, what is rear after enqueue?
Q3: Which pointer does a linked-list queue need for O(1) enqueue?
Challenge: BFS Visit Order
What order does BFS visit this tree?
Tree:
Starting from node 1, children are visited left-to-right.
BFS Trace
Step Dequeue Enqueue Children Queue
──── ─────── ─────────────── ─────
1 1 2, 3 [2, 3]
2 2 4, 5 [3, 4, 5]
3 3 6, 7 [4, 5, 6, 7]
4 4 (none) [5, 6, 7]
5 5 (none) [6, 7]
6 6 (none) [7]
7 7 (none) []
Visit order: 1, 2, 3, 4, 5, 6, 7
Level 0: 1
Level 1: 2, 3
Level 2: 4, 5, 6, 7
Challenge: Predict the Winner
Players: [A, B, C, D], k=2. Who wins?
Trace it yourself:
Start: queue = [A, B, C, D]
Each round: pass k=2 times (dequeue+re-enqueue), then eliminate.
Full Trace
Round 1: [A, B, C, D]
pass 1: A→back → [B, C, D, A]
pass 2: B→back → [C, D, A, B]
eliminate: C out! → [D, A, B]
Round 2: [D, A, B]
pass 1: D→back → [A, B, D]
pass 2: A→back → [B, D, A]
eliminate: B out! → [D, A]
Round 3: [D, A]
pass 1: D→back → [A, D]
pass 2: A→back → [D, A]
eliminate: D out! → [A]
Winner: A!
Hint
Dequeue+re-enqueue k times = rotate the queue k positions. Then the front person is eliminated. Trace carefully!