Queue: with a circular array and with a linked list
FIFO logic, enqueue/dequeue/peek, a circular buffer that prevents the array from drifting toward the start, and a linked queue with head/tail. The exam asks for the raw state of the array and the front/rear values after a sequence of operations.
A queue is the checkout line at a supermarket: you join at the back, the one in front leaves. The first to arrive is served first: FIFO (First In, First Out). Printer queues, an operating system’s job queue, packets in a network and matchmaking queues in games are all queues. The only difference from a stack is which end you remove from, but implementing it is more work: you have to track both ends. On the exam, the raw state of a circular array (what is in which cell, what front and rear are) is asked almost every year.
Golden rules:
- Add at the back (enqueue), remove from the front (dequeue).
- Don’t shift elements in the array. Advance the front and rear indices, and wrap around to the start with
%when you reach the end. - front == rear can mean both empty and full. Keep a
countto tell them apart. - In a linked queue, when the last element comes out, set tail to null too.
The queue ADT
| Operation | What it does | Time |
|---|---|---|
enqueue(x) | adds x at the back | O(1) |
dequeue() | removes and returns the front element | O(1) |
peek() (or front()) | returns the front element, doesn’t remove it | O(1) |
isEmpty(), size() | status | O(1) |
Comparison with a stack: if 1, 2, 3 are inserted in order and then all are removed
stack: 3 2 1 | queue: 1 2 3
Why isn’t a simple array enough?
The first idea: always keep the front at index 0, and on removal shift everyone one step left. It works correctly, but every dequeue does n − 1 moves. The total cost of removing n elements one by one:
n=1000 shifts=499500
n=2000 shifts=1999000
n=4000 shifts=7998000
When n doubles the cost quadruples: O(n²) in total, O(n) per operation. If you advance the front index instead of shifting, this time the start of the array is wasted, and when rear reaches the end it looks as though there is no room left. The solution: think of the array as a ring.
The circular array (circular buffer)
When an index reaches the end it wraps to 0: i = (i + 1) % capacity. For capacity 4 the index order is 0, 1, 2, 3, 0, 1, …
class ArrayQueue {
private int[] data;
private int front = 0; // index of the first element
private int rear = 0; // the next free slot
private int count = 0;
ArrayQueue(int capacity) { data = new int[capacity]; }
boolean isEmpty() { return count == 0; }
boolean isFull() { return count == data.length; }
void enqueue(int x) {
if (isFull()) throw new RuntimeException("Queue overflow");
data[rear] = x;
rear = (rear + 1) % data.length;
count++;
}
int dequeue() {
if (isEmpty()) throw new RuntimeException("Queue underflow");
int x = data[front];
front = (front + 1) % data.length;
count--;
return x;
}
int peek() {
if (isEmpty()) throw new RuntimeException("Queue underflow");
return data[front];
}
}
PITFALL · the meaning of rear. In some books rear is the index of the last element, in others the next free slot. On this page it is the next free slot. On the exam, read the definition in the question; the formulas shift by one accordingly.
Worked trace (capacity 4)
ArrayQueue q = new ArrayQueue(4);
q.enqueue(10); q.enqueue(20); q.enqueue(30);
System.out.println(q.dequeue());
System.out.println(q.raw());
q.enqueue(40); q.enqueue(50);
System.out.println(q.raw());
System.out.println(q.dequeue() + " " + q.dequeue());
System.out.println(q.raw());
10
[10, 20, 30, 0] front=1 rear=3 count=2
[50, 20, 30, 40] front=1 rear=1 count=4
20 30
[50, 20, 30, 40] front=3 rear=1 count=2
enqueue 10,20,30 [10,20,30, _] front=0 rear=3 count=3
dequeue → 10 [10,20,30, _] front=1 rear=3 count=2 (10 stayed in the array)
enqueue 40 [10,20,30,40] front=1 rear=0 count=3 (rear 3 → (3+1)%4 = 0)
enqueue 50 [50,20,30,40] front=1 rear=1 count=4 (written over the 10, full)
dequeue → 20 front=2
dequeue → 30 front=3, count=2; the queue logically: 40, 50
In the final state the queue consists of 40 and 50. To read from the front, start at front and advance count times with (i + 1) % cap: index 3 (40), index 0 (50).
TIP · Raw array questions. When the exam asks “what are the contents of the array?”, the removed elements are still written in the array. Only a new enqueue overwrites them. At each step write four things: array, front, rear, count.
Queue with a linked list (linked queue)
In a singly linked list, insertion at the head and deletion at the head are O(1), insertion at the end is O(1) if there is a tail, and deletion from the end is O(n). Therefore: insert at the tail, remove at the head.
class LinkedQueue {
private static class Node { int data; Node next; Node(int d) { data = d; } }
private Node head, tail; // head = front, tail = back
boolean isEmpty() { return head == null; }
void enqueue(int x) {
Node n = new Node(x);
if (tail == null) head = tail = n; // empty queue
else { tail.next = n; tail = n; }
}
int dequeue() {
if (head == null) throw new RuntimeException("Queue underflow");
int x = head.data;
head = head.next;
if (head == null) tail = null; // the queue became empty
return x;
}
}
If the line if (head == null) tail = null; is forgotten: after the queue empties, tail keeps pointing to the old node, the next enqueue is linked behind that dead node, and head stays null. The queue looks empty forever.
| Circular array | Linked queue | |
|---|---|---|
| enqueue / dequeue | O(1) | O(1) |
| Capacity | fixed (or growing) | unbounded |
| Memory | contiguous, empty cells | next per node |
| Risk of error | forgetting %, empty/full confusion | forgetting to update tail |
Queue patterns
Rotating. q.enqueue(q.dequeue()) moves the front element to the back. If you do it k times the queue rotates k steps: 10 20 30 40 50 twice → 30 40 50 10 20.
Reversing. Empty the queue into a stack, then fill it back from the stack into the queue: 1 2 3 4 → 4 3 2 1.
Processing in turn. Taking the front two elements and adding the result to the back sets up a “tournament” or merge operation. The number of elements decreases by one each round; stop when one element remains.
Common mistakes
| Wrong | Correct |
|---|---|
| Shifting elements in dequeue | Advance front: O(1) |
rear = rear + 1 | rear = (rear + 1) % data.length |
If front == rear it is empty | Look at count; it can be full too |
| The removed value is erased from the array | It stays, and is overwritten later |
| Removing from the tail in a linked queue | Remove from the head, insert at the tail |
| Leaving the tail when the last element comes out | if (head == null) tail = null; |
| Setting only the tail when inserting into an empty linked queue | head = tail = n |
| Mixing up queue and stack removal order | A queue preserves order, a stack reverses it |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
What is printed? (q is an empty queue.)
q.enqueue(5); q.enqueue(3);
System.out.print(q.dequeue() + " ");
q.enqueue(8); q.enqueue(1);
System.out.print(q.dequeue() + " ");
System.out.print(q.dequeue() + " ");
q.enqueue(6);
System.out.println(q.peek());Show answer
[5,3] → dequeue 5 → [3] → [3,8,1] → dequeue 3 → dequeue 8 → [1] → [1,6], peek gives the front: 1. If the same operations had been done with a stack (the question in week 5), 3 1 8 6 would have been printed.
The values 1, 2, 3 are inserted into a stack and into a queue, then everything is removed. What is the removal order for each?
Show answer
The stack is LIFO: the last in, 3, comes out first. The queue is FIFO: the first in, 1, comes out first. The queue preserves the order, the stack reverses it.
A circular array queue has capacity 5, front = rear = count = 0. enqueue writes at rear then rear = (rear + 1) % 5; dequeue reads at front then front = (front + 1) % 5. After the calls below, give the array, front, rear and count.
enqueue(1); enqueue(2); enqueue(3); enqueue(4);
dequeue(); dequeue(); dequeue();
enqueue(5); enqueue(6); enqueue(7);Show answer
Four enqueues: [1,2,3,4,_], rear 4. Three dequeues: front 3, count 1 (only 4 remains). enqueue(5) goes to index 4, rear (4+1)%5 = 0. enqueue(6) goes to index 0 (over the 1), rear 1. enqueue(7) goes to index 1, rear 2. The old 3 stays in the array but is not in the queue. The removal order: 4 5 6 7.
Same circular queue design with capacity 4. What are the array, front, rear and count after these calls? Is the queue full?
for (int i = 1; i <= 4; i++) enqueue(i);
dequeue(); dequeue();
enqueue(5); enqueue(6);Show answer
[1,2,3,4], rear (3+1)%4 = 0. Two dequeues: front 2. 5 goes to index 0, 6 to index 1, rear 2. front == rear but since count = 4 it is full. front == rear alone doesn't say whether it is empty or full; that is why count is kept.
A naive array queue keeps the front element at index 0 and shifts all elements left on every dequeue. What is the cost of one dequeue, and why is the circular version better?
Show answer
Shifting moves n − 1 elements. Removing n elements one by one costs n(n−1)/2 moves in total: 499 500 for n = 1000, 1 999 000 for n = 2000 (n² growth). In a circular array only the front index advances and no element shifts: O(1).
Write void enqueue(int x) and int dequeue() for a circular array queue with fields int[] data; int front, rear, count; (rear = next free slot).
Show answer
void enqueue(int x) {
if (count == data.length)
throw new RuntimeException("Queue overflow");
data[rear] = x;
rear = (rear + 1) % data.length; // jump to 0 at the end
count++;
}
int dequeue() {
if (count == 0)
throw new RuntimeException("Queue underflow");
int x = data[front];
front = (front + 1) % data.length;
count--;
return x;
}Without % data.length, when rear passes the end of the array you get an ArrayIndexOutOfBoundsException, although there is free space at the start.Write int dequeue() for a linked queue with Node head (front) and Node tail (rear). Make sure the queue still works after it becomes empty.
Show answer
int dequeue() {
if (head == null)
throw new RuntimeException("Queue underflow");
int x = head.data;
head = head.next;
if (head == null) // the last element came out
tail = null;
return x;
}Insertion is done at the end (tail), removal at the front (head). Both are O(1). If you did the reverse (removal from the end), it would be O(n) in a singly linked list.In this buggy linked queue, dequeue forgets to set tail = null when the queue becomes empty. What does the queue contain after the calls below?
void enqueue(int x) {
Node n = new Node(x);
if (tail == null) head = tail = n;
else { tail.next = n; tail = n; }
}
int dequeue() { int x = head.data; head = head.next; return x; }
enqueue(1); dequeue(); enqueue(2); enqueue(3);Show answer
After dequeue, head is null but tail still points to the old node 1. Since tail is not null, enqueue(2) links 2 behind the old node, and head stays null. 3 is added to the same chain. Looking from head, the queue is empty: 2 and 3 are lost.
The queue contains (front first) 10 20 30 40 50. What does it contain after this loop?
for (int k = 0; k < 2; k++)
q.enqueue(q.dequeue());Show answer
In each round the front element is moved to the back. 10 to the back: 20 30 40 50 10. 20 to the back: 30 40 50 10 20. The pattern of rotating the queue k times.
The queue contains 1 2 3 4 (front first). What does it contain after this code?
Deque<Integer> st = new ArrayDeque<>();
while (!q.isEmpty()) st.push(q.dequeue());
while (!st.isEmpty()) q.enqueue(st.pop());Show answer
Items come out of the queue in the order 1 2 3 4 and are pushed onto the stack; the top is 4. They come out of the stack in the order 4 3 2 1 and enter the queue. A stack is the standard way to reverse a queue.
A circular array queue of capacity 3 runs this loop. What is printed?
try {
for (int i = 1; i <= 4; i++) {
q.enqueue(i * 10);
System.out.print(i * 10 + " ");
}
} catch (RuntimeException e) {
System.out.println("-> " + e.getMessage());
}Show answer
Three insertions succeed, and count 3 = capacity. The fourth enqueue is stopped by the full check; the catch runs without printing 40.
The queue contains 4 9 2 7 (front first). What is printed?
int total = 0;
while (q.size() > 1) {
int x = q.dequeue();
int y = q.dequeue();
q.enqueue(x + y);
total += x + y;
}
System.out.println(q.peek() + " " + total);Show answer
4 + 9 = 13 → [2,7,13], total 13. 2 + 7 = 9 → [13,9], total 22. 13 + 9 = 22 → [22], total 44. The output: 22 44.
Verification: ArrayQueue (a circular array with front, rear, count), LinkedQueue and the shifting NaiveQueue were run on the JDK in verify/SEN2211/W07.java (java W07.java). The raw state of the arrays and the front/rear/count values are taken verbatim from the program's raw() output; the cost of the shifting queue was counted for n = 1000, 2000, 4000 and compared with n²/2.