Implementing the Queue
A Queue follows FIFO (First In, First Out). It is essential for breadth-first search (BFS), printer spools, and web server request handling.
1. Core Operations
All Queue operations must be O(1).
- Enqueue(val): Add
valto the back. - Dequeue(): Remove and return the front element.
- Peek(): Return the front element.
- IsEmpty(): Check if empty.
2. Implementation Strategies
A. Linked List Implementation
- Head: Front of Queue (Dequeue here).
- Tail: Back of Queue (Enqueue here).
- Pros: Infinite size.
- Cons: Dynamic allocation overhead.
B. Array Implementation (The Trap)
If you use a simple array [A, B, C]:
- Dequeue
A→[_, B, C] - Dequeue
B→[_, _, C] - Problem: You have empty space at the front but run out of space at the back!
- Solution: Ring Buffer (Circular Queue).
3. The Mathematical Anchor: Circular Queue (Modular Arithmetic)
How do we reuse space in a fixed-size array without expensive O(N) shifts? We use Modular Arithmetic.
The Wrap-Around Formula
If our array has size N:
- Enqueue:
tail = (tail + 1) % N - Dequeue:
head = (head + 1) % N
Why it works
The % operator keeps our indices bounded between [0, N-1]. When tail reaches index 7 in an 8-size array, (7 + 1) % 8 = 0. The pointer “warps” back to the start.
The “Full” vs “Empty” Dilemma
How do we tell if the queue is full or empty if head == tail in both cases?
- Count approach: Keep a separate
countvariable (Simplest). - Size-1 approach: Always keep one slot empty. Full is
(tail + 1) % size == head.
4. Interactive: Ring Buffer Visualizer
A fixed-size array that wraps around. When the tail hits the end, it goes back to index 0.
Empty
Head Index: 0 | Tail Index: 0
5. Implementation
Java
// Using Java's built-in LinkedList as a Queue
import java.util.LinkedList;
import java.util.Queue;
public class QueueDemo {
public void demo() {
Queue<Integer> q = new LinkedList<>();
q.offer(10); // Enqueue
q.offer(20);
int front = q.peek(); // 10
int removed = q.poll(); // 10
// ArrayDeque is also valid and faster!
}
}
Go
type Queue []int
func (q *Queue) Enqueue(v int) {
*q = append(*q, v)
}
func (q *Queue) Dequeue() int {
if len(*q) == 0 { return -1 }
val := (*q)[0]
*q = (*q)[1:] // Slicing off front
return val
}