B2.2.4 Explain the concept of a queue as a “first in, first out” (FIFO) data structure.
• Must include fundamental operations such as enqueue, dequeue, front and isEmpty
• How queue operations impact both performance and memory usage
• An appropriate queue for a specific problem
The Big Idea
A queue is a linear abstract data type (ADT) that adheres to the First In, First Out (FIFO) access policy. This means the first element added to the queue is the first element removed. The queue structure is fundamental in computing systems for modeling real-world waiting lines, message passing, and task scheduling where processing order must be preserved.
1. Core Characteristics
In a FIFO structure, data elements are inserted at the rear (tail) and removed from the front (head). The interface of a queue typically supports the following fundamental operations:
| Operation | Description |
|---|---|
enqueue(x) | Inserts element x at the rear (tail) of the queue |
dequeue() | Removes and returns the element at the front (head) of the queue |
front() | Returns (but does not remove) the front element in the queue. Front is the same thing as peek(). |
isEmpty() | Returns a Boolean indicating whether the queue contains any elements |
All operations are expected to execute in constant time O(1), depending on the implementation.
2. Queue Implementations and Performance
Array-based Implementation (Circular Queue)
- Fixed-size buffer.
- Uses modular arithmetic to wrap front and rear pointers.
- Supports O(1) enqueue and dequeue with efficient memory usage.
# Python pseudo-code for circular queue
class CircularQueue:
def __init__(self, capacity):
self.queue = [None] * capacity
self.front = 0
self.rear = 0
self.size = 0
self.capacity = capacity
def enqueue(self, value):
if self.size == self.capacity:
raise OverflowError("Queue is full")
self.queue[self.rear] = value
self.rear = (self.rear + 1) % self.capacity
self.size += 1
def dequeue(self):
if self.size == 0:
raise IndexError("Queue is empty")
value = self.queue[self.front]
self.front = (self.front + 1) % self.capacity
self.size -= 1
return value
Linked List Implementation
- Dynamically sized.
- Each node stores data and a reference to the next node.
- Requires O(1) time for enqueue and dequeue with no shifting, but incurs pointer overhead.
class Node {
int value;
Node next;
Node(int val) { this.value = val; this.next = null; }
}
class LinkedQueue {
Node front, rear;
void enqueue(int x) {
Node node = new Node(x);
if (rear == null) {
front = rear = node;
} else {
rear.next = node;
rear = node;
}
}
int dequeue() {
if (front == null) throw new RuntimeException("Queue is empty");
int val = front.value;
front = front.next;
if (front == null) rear = null;
return val;
}
}
3. Memory and Performance Considerations
| Characteristic | Array-based Queue | Linked List Queue |
|---|---|---|
| Time complexity | O(1) for enqueue, dequeue | O(1) for enqueue, dequeue |
| Space complexity | O(n) fixed size or resize required | O(n) with per-node pointer overhead |
| Memory efficiency | Good (contiguous block) | Moderate (fragmented, pointer storage) |
| Cache locality | Excellent | Poor |
| Growth model | Fixed or resize on overflow | Unbounded, dynamic memory allocation |
- isEmpty checks are performed in O(1) using size counters or pointer comparisons.
- Excessive
enqueue/dequeueoperations on array-based queues without wrapping will require shifting (unless circular logic is applied), which would degrade performance to O(n).
4. Appropriate Use Case for a Queue
Use Case: Print Queue in an Operating System
In a multi-tasking OS, print jobs are submitted by different processes. These must be serviced in the exact order received to ensure fairness and data integrity.
- enqueue: A new job is appended to the print queue when submitted.
- dequeue: The printer service removes and processes the first job in line.
- front: Used to display or preview the next job to print.
- isEmpty: Checked to determine if the printer is idle.
Why a queue is appropriate:
- Preserves strict first-come, first-served order.
- Supports dynamic number of jobs.
- Efficient to manage with minimal overhead.
5. Broader Applications of Queues
| Application | Role of Queue |
|---|---|
| Breadth-first search (BFS) | Traverses graph layers in order |
| Task scheduling | Maintains order of jobs in a CPU |
| Buffering (I/O) | Stores data from slow producer to fast consumer |
| Network packets | Processes incoming data sequentially |
| Customer service systems | Ensures fair handling of clients |
6. Summary
| Property | Queue |
|---|---|
| Access policy | First In, First Out (FIFO) |
| Fundamental operations | enqueue, dequeue, front, isEmpty |
| Time complexity (all ops) | O(1) |
| Typical implementations | Circular array, linked list |
| Memory trade-offs | Arrays are fast but fixed; links are flexible but heavier |
| Real-world analogy | Line at a ticket counter |
| Common use cases | BFS, OS schedulers, stream buffers |
Final Thoughts
The queue is a simple but critical structure for managing sequential processing in both algorithmic and system-level contexts. Its strict FIFO discipline ensures fairness and predictability, and its operations are highly performant when implemented correctly. Understanding the nuances of queue implementation and memory behavior is essential for designing efficient, scalable systems that rely on order-preserving computation.