
Queue Data Structure Explained
A Queue is a linear data structure that follows the First In, First Out principle, also known as FIFO. This means that the first element added to the queue is the first element removed from it.
Queues are very important in computer science and software engineering. They are used in task scheduling, printer queues, message queues, request handling, breadth-first search, background jobs, operating systems, and many real software systems.
Introduction
Data structures help developers organize data according to how that data should be accessed and processed. Arrays, linked lists, stacks, queues, trees, graphs, and hash tables all support different access patterns.
A Queue is one of the most practical data structures because it models waiting lines. The first item that enters the queue should be the first item that leaves. This behavior is common in real life and in software systems.
For example, customers waiting at a service desk are usually served in the order they arrived. A printer handles documents in the order they were sent. A server may process requests in the order they arrive. These are all queue-like behaviors.
What Is a Queue?
A Queue is a collection of elements where insertion happens at one end and removal happens from the other end.
The end where elements are added is usually called the rear or back of the queue. The end where elements are removed is called the front of the queue.
The Queue follows the FIFO rule:
First In, First OutThis means the element that has waited the longest is processed first.
The main idea is simple:
Add new elements at the rear.
Remove elements from the front.
Read the front element without removing it when needed.
Check whether the queue is empty before removing an element.
A Queue is designed for ordered processing, not random access.
Real-Life Analogy of a Queue
A common real-life example of a Queue is a line of people waiting at a cashier.
Front → Person A → Person B → Person C ← RearPerson A arrived first, so Person A is served first. Person C arrived last, so Person C waits behind the others.
If a new person arrives, they join at the rear:
Front → Person A → Person B → Person C → Person D ← RearWhen the cashier serves someone, Person A leaves first:
Front → Person B → Person C → Person D ← RearThis is exactly how a Queue works in programming.
The FIFO Principle
FIFO stands for First In, First Out. It means that the first item inserted into the queue is the first item removed from the queue.
For example, if we enqueue values in this order:
10, 20, 30The queue looks like this:
Front → 10 → 20 → 30 ← RearIf we dequeue an element, the value 10 is removed first because it entered the queue first.
This behavior is different from a Stack, which follows LIFO, or Last In, First Out.
Basic Queue Operations
A Queue usually supports a small group of important operations.
enqueue: Adds an element to the rear of the queue.
dequeue: Removes and returns the front element.
front: Returns the front element without removing it.
rear: Returns the rear element without removing it.
isEmpty: Checks whether the queue has no elements.
size: Returns the number of elements in the queue.
These operations make Queues useful whenever items must be processed in arrival order.
Enqueue Operation
The enqueue operation adds a new element to the rear of the queue.
If the queue is empty and we enqueue 10, the queue becomes:
Front → 10 ← RearIf we enqueue 20, the queue becomes:
Front → 10 → 20 ← RearIf we enqueue 30, the queue becomes:
Front → 10 → 20 → 30 ← RearEvery new element is added after the current rear element.
Dequeue Operation
The dequeue operation removes the front element from the queue and returns it.
Consider this queue:
Front → 10 → 20 → 30 ← RearIf we call dequeue, the value 10 is removed.
Removed: 10The queue becomes:
Front → 20 → 30 ← RearIf we call dequeue again, the value 20 is removed.
Removed: 20The queue becomes:
Front → 30 ← RearThe dequeue operation must be used carefully when the queue is empty. Removing from an empty queue is usually called queue underflow.
Front Operation
The front operation returns the first element in the queue without removing it.
Consider this queue:
Front → 10 → 20 → 30 ← RearIf we call front, the result is:
10The queue remains unchanged:
Front → 10 → 20 → 30 ← RearThis operation is useful when we need to inspect the next item that will be processed.
Rear Operation
The rear operation returns the last element in the queue without removing it.
Consider this queue:
Front → 10 → 20 → 30 ← RearIf we call rear, the result is:
30The queue remains unchanged.
Rear is useful when we need to inspect the most recently added element.
isEmpty Operation
The isEmpty operation checks whether the queue contains any elements.
If the queue has no elements:
[]Then isEmpty returns true.
If the queue contains elements:
[10, 20, 30]Then isEmpty returns false.
This operation is important before calling dequeue, front, or rear.
Manual Run Example
Let us manually run a sequence of Queue operations.
enqueue(10)
enqueue(20)
enqueue(30)
front()
dequeue()
enqueue(40)
dequeue()
rear()We start with an empty queue:
Queue: []Step 1: enqueue(10)
We add 10 to the rear of the queue.
Front → 10 ← RearStep 2: enqueue(20)
We add 20 after 10.
Front → 10 → 20 ← RearStep 3: enqueue(30)
We add 30 after 20.
Front → 10 → 20 → 30 ← RearStep 4: front()
The front element is 10.
front() returns 10The queue does not change:
Front → 10 → 20 → 30 ← RearStep 5: dequeue()
The front element 10 is removed.
dequeue() returns 10The queue becomes:
Front → 20 → 30 ← RearStep 6: enqueue(40)
We add 40 to the rear.
Front → 20 → 30 → 40 ← RearStep 7: dequeue()
The front element 20 is removed.
dequeue() returns 20The queue becomes:
Front → 30 → 40 ← RearStep 8: rear()
The rear element is 40.
rear() returns 40The queue remains unchanged:
Front → 30 → 40 ← RearThis manual run shows FIFO behavior clearly. The first value added is removed first.
Queue Implementation Using an Array
A Queue can be implemented using an array. The simplest approach is to add elements at the end of the array and remove elements from the beginning.
For example:
[10, 20, 30]The value 10 is the front, and the value 30 is the rear.
However, removing from the beginning of an array can be inefficient in some languages because remaining elements may need to shift. A better implementation uses a front index to avoid shifting on every dequeue.
Queue Pseudocode
class Queue:
items = empty array
frontIndex = 0
enqueue(value):
add value to end of items
dequeue():
if queue is empty:
return error
value = items[frontIndex]
frontIndex = frontIndex + 1
return value
front():
if queue is empty:
return error
return items[frontIndex]
isEmpty():
return frontIndex >= length of items
size():
return length of items - frontIndexThis version avoids removing elements from the beginning of the array directly.
Queue Example in PHP
Here is a clean PHP implementation of a Queue using an array and a front index.
<?php
class Queue
{
private array $items = [];
private int $frontIndex = 0;
public function enqueue(mixed $value): void
{
$this->items[] = $value;
}
public function dequeue(): mixed
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot dequeue from an empty queue.');
}
$value = $this->items[$this->frontIndex];
$this->frontIndex++;
return $value;
}
public function front(): mixed
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot read front from an empty queue.');
}
return $this->items[$this->frontIndex];
}
public function rear(): mixed
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot read rear from an empty queue.');
}
return $this->items[count($this->items) - 1];
}
public function isEmpty(): bool
{
return $this->frontIndex >= count($this->items);
}
public function size(): int
{
return count($this->items) - $this->frontIndex;
}
}
$queue = new Queue();
$queue->enqueue(10);
$queue->enqueue(20);
$queue->enqueue(30);
echo $queue->front(); // 10
echo PHP_EOL;
echo $queue->dequeue(); // 10
echo PHP_EOL;
$queue->enqueue(40);
echo $queue->rear(); // 40This implementation avoids using array_shift, which can be inefficient for large arrays because it may shift elements internally.
Queue Example in JavaScript
Here is a JavaScript implementation using an array and a front index.
class Queue {
constructor() {
this.items = [];
this.frontIndex = 0;
}
enqueue(value) {
this.items.push(value);
}
dequeue() {
if (this.isEmpty()) {
throw new Error('Cannot dequeue from an empty queue.');
}
const value = this.items[this.frontIndex];
this.frontIndex++;
return value;
}
front() {
if (this.isEmpty()) {
throw new Error('Cannot read front from an empty queue.');
}
return this.items[this.frontIndex];
}
rear() {
if (this.isEmpty()) {
throw new Error('Cannot read rear from an empty queue.');
}
return this.items[this.items.length - 1];
}
isEmpty() {
return this.frontIndex >= this.items.length;
}
size() {
return this.items.length - this.frontIndex;
}
}
const queue = new Queue();
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
console.log(queue.front()); // 10
console.log(queue.dequeue()); // 10
queue.enqueue(40);
console.log(queue.rear()); // 40This JavaScript version avoids using shift() for every dequeue. Using shift() can be less efficient because it may move all remaining elements.
Queue Implementation Using a Linked List
A Queue can also be implemented using a Linked List. In this implementation, the queue maintains both a front reference and a rear reference.
The front points to the node that will be removed next. The rear points to the node where new elements are added.
Front → [10] → [20] → [30] ← RearWith both front and rear references, enqueue and dequeue can both be O(1).
Linked List Queue Example in JavaScript
class QueueNode {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedListQueue {
constructor() {
this.frontNode = null;
this.rearNode = null;
this.length = 0;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (this.rearNode === null) {
this.frontNode = newNode;
this.rearNode = newNode;
} else {
this.rearNode.next = newNode;
this.rearNode = newNode;
}
this.length++;
}
dequeue() {
if (this.isEmpty()) {
throw new Error('Cannot dequeue from an empty queue.');
}
const removedValue = this.frontNode.data;
this.frontNode = this.frontNode.next;
if (this.frontNode === null) {
this.rearNode = null;
}
this.length--;
return removedValue;
}
front() {
if (this.isEmpty()) {
throw new Error('Cannot read front from an empty queue.');
}
return this.frontNode.data;
}
rear() {
if (this.isEmpty()) {
throw new Error('Cannot read rear from an empty queue.');
}
return this.rearNode.data;
}
isEmpty() {
return this.frontNode === null;
}
size() {
return this.length;
}
}This linked-list-based implementation is efficient because it does not need shifting or index movement.
Array Queue vs Linked List Queue
Both arrays and linked lists can be used to implement a Queue.
An array-based queue is simple and convenient, especially in high-level languages. A linked-list-based queue is useful when we want efficient dynamic insertion and removal without worrying about shifting elements.
Main differences include:
Array queue is easy to implement.
Linked list queue uses nodes and references.
Array queue should avoid expensive front removal operations.
Linked list queue can support O(1) enqueue and dequeue with front and rear references.
Array queue may use unused space if a front index keeps increasing.
Linked list queue uses extra memory for node references.
For learning, both implementations are useful. For production, built-in queue structures or optimized libraries may be better.
Types of Queues
There are several types of Queues. Each type solves a different kind of problem.
The main types are:
Simple Queue.
Circular Queue.
Priority Queue.
Deque.
Understanding these types helps developers choose the correct queue structure for different situations.
Simple Queue
A Simple Queue follows the normal FIFO rule. Elements are inserted at the rear and removed from the front.
Front → 10 → 20 → 30 ← RearThis is the standard queue used in most beginner examples.
Simple Queues are useful for:
Basic task processing.
Request handling.
Order-based processing.
Teaching FIFO behavior.
Circular Queue
A Circular Queue connects the end of the queue back to the beginning logically. It is often implemented using a fixed-size array.
The main idea is that when the rear reaches the end of the array, it can wrap around to the beginning if there is free space.
Array size: 5
[_, 20, 30, 40, _]
front rearIf space exists at the beginning, the rear can move there:
[50, 20, 30, 40, _]
rear frontCircular Queues are useful when working with fixed-size buffers, streaming data, and operating system scheduling.
Priority Queue
A Priority Queue removes elements based on priority rather than arrival order.
For example, in a hospital emergency system, a critical patient may be treated before someone who arrived earlier but has a less urgent condition.
Task A: priority 2
Task B: priority 5
Task C: priority 1If higher priority values are processed first, Task B is removed before Task A and Task C.
Priority Queues are often implemented using heaps because heaps provide efficient insertion and removal of the highest or lowest priority item.
Deque
A Deque, pronounced double-ended queue, allows insertion and removal from both ends.
Front ⇄ [10] ⇄ [20] ⇄ [30] ⇄ RearA Deque can behave like a Queue or like a Stack depending on how it is used.
Deque operations include:
Insert at front.
Insert at rear.
Remove from front.
Remove from rear.
Deques are useful in sliding window algorithms, undo-redo systems, and advanced scheduling problems.
Time Complexity of Queue Operations
Time complexity describes how the running time of an operation grows as the number of elements increases.
For a standard Queue with a good implementation, common complexities are:
enqueue: O(1)
dequeue: O(1)
front: O(1)
rear: O(1)
isEmpty: O(1)
size: O(1)
search: O(n)
Enqueue and dequeue are constant-time operations when the queue is implemented correctly.
Why Enqueue Is O(1)
The enqueue operation adds a new element at the rear of the queue.
Before:
Front → 10 → 20 ← Rear
enqueue(30)
After:
Front → 10 → 20 → 30 ← RearIf the rear position is known, adding the new element does not require scanning the whole queue.
Therefore, enqueue is:
O(1)Why Dequeue Is O(1)
The dequeue operation removes the front element.
Before:
Front → 10 → 20 → 30 ← Rear
dequeue()
After:
Front → 20 → 30 ← RearIf the front position is known, removing the front element can be done directly.
Therefore, dequeue is:
O(1)This assumes a good implementation. If an array implementation uses a costly shifting operation every time, dequeue may become O(n).
Why Search Is O(n)
Searching inside a Queue is not the main purpose of the structure. If we need to find a specific value, we may need to inspect elements one by one.
Front → 10 → 20 → 30 ← RearTo search for 30, we may need to check 10, then 20, then 30.
In the worst case, all elements are checked, so search is:
O(n)Space Complexity of a Queue
The space complexity of a Queue is O(n), where n is the number of elements stored in the queue.
If the queue stores 100 items, it needs memory for 100 items. If it stores 1,000 items, it needs memory for 1,000 items.
Space complexity = O(n)If the queue is implemented using linked nodes, each node also stores a reference to the next node. If it is implemented using an array, memory depends on the array capacity and implementation strategy.
Queue Overflow and Queue Underflow
Queue overflow happens when a queue has a fixed capacity and a program tries to insert more elements than the queue can hold.
Queue capacity: 3
Queue: [10, 20, 30]
enqueue(40) → overflowQueue underflow happens when a program tries to remove or read an element from an empty queue.
Queue: []
dequeue() → underflowGood Queue implementations should handle these cases safely by throwing an exception, returning null, or using a clear error strategy.
Queues and Breadth-First Search
One of the most important algorithmic uses of a Queue is Breadth-First Search, also known as BFS.
BFS explores nodes level by level. A Queue is used to remember which node should be processed next.
For example, in a graph or tree:
Start with node A
enqueue(A)
While queue is not empty:
dequeue a node
visit it
enqueue its unvisited neighborsThe FIFO behavior ensures that nodes discovered earlier are processed earlier, which creates level-order traversal.
Queues and Task Scheduling
Queues are used in task scheduling systems where tasks should be processed in the order they arrive.
For example:
Task 1: Send email
Task 2: Generate report
Task 3: Resize imageA worker can process tasks from the front of the queue:
dequeue() → Send email
dequeue() → Generate report
dequeue() → Resize imageThis model is common in background job systems and message brokers.
Queues and Printer Jobs
A printer queue is a classic Queue example. If three documents are sent to a printer, they are usually printed in the order they arrived.
Front → Document A → Document B → Document C ← RearDocument A is printed first, then Document B, then Document C.
This is FIFO behavior in a real system.
Queues and Web Requests
Queues are also used in web systems. When many requests or background jobs arrive, a queue can hold them until workers are ready to process them.
For example, a Laravel application may use queues for:
Sending emails.
Processing uploaded files.
Generating reports.
Sending notifications.
Running slow tasks in the background.
This prevents slow operations from blocking the main user request.
Practical Example: Simple Task Queue in JavaScript
Here is a simple task queue example:
class TaskQueue {
constructor() {
this.queue = [];
this.frontIndex = 0;
}
addTask(task) {
this.queue.push(task);
}
processTask() {
if (this.frontIndex >= this.queue.length) {
return null;
}
const task = this.queue[this.frontIndex];
this.frontIndex++;
return task;
}
}
const tasks = new TaskQueue();
tasks.addTask('Send email');
tasks.addTask('Generate report');
tasks.addTask('Resize image');
console.log(tasks.processTask()); // Send email
console.log(tasks.processTask()); // Generate reportThe first task added is the first task processed.
Queue vs Stack
Queues and Stacks are both linear data structures, but they follow different access rules.
A Queue follows FIFO:
First In, First OutA Stack follows LIFO:
Last In, First OutFor example, a Queue is like a line of people waiting. The first person who arrives is served first.
A Stack is like a pile of plates. The last plate placed on top is removed first.
Main differences include:
Queue removes the oldest item first.
Stack removes the newest item first.
Queue uses enqueue and dequeue.
Stack uses push and pop.
Queue is useful for scheduling and waiting lines.
Stack is useful for undo, recursion, and backtracking.
When Should Developers Use a Queue?
Developers should use a Queue when items need to be processed in the same order they arrive.
Queues are useful when:
First-in, first-out behavior is required.
Tasks should be processed by arrival order.
Requests need to wait before processing.
Breadth-first search is needed.
Background jobs are used.
Message processing is needed.
Scheduling or buffering is required.
The main signal is FIFO behavior. If the oldest item should be processed first, a Queue may be the correct structure.
When Not to Use a Queue
A Queue is not the right structure when the newest item should be processed first, or when random access is required.
Developers should avoid a Queue when:
Last-in, first-out behavior is required.
Fast random access by index is needed.
Priority-based processing is required.
Searching frequently is the main operation.
The order of arrival does not matter.
In those cases, a Stack, Array, Hash Table, Priority Queue, or another data structure may be more suitable.
Common Mistakes When Learning Queues
Beginners often understand the FIFO idea but make mistakes in implementation or complexity analysis.
Common mistakes include:
Confusing Queue with Stack.
Removing from the wrong end.
Using costly array shifting without understanding performance.
Forgetting to handle empty queue cases.
Assuming search is O(1).
Not updating front and rear correctly in linked-list queues.
Creating wrong wrap-around logic in circular queues.
The safest rule is simple: enqueue at the rear and dequeue from the front.
Practical Checklist for Understanding Queues
Before moving to advanced data structures and graph algorithms, developers should be able to answer these questions:
What does FIFO mean?
What is the front of a Queue?
What is the rear of a Queue?
What does enqueue do?
What does dequeue do?
Why are enqueue and dequeue O(1) in a good implementation?
What is queue underflow?
What is the difference between a Queue and a Stack?
What is a Circular Queue?
Why does BFS use a Queue?
If these questions are clear, the developer understands the real logic behind Queues.
Advantages of Queues
Queues have several important advantages.
They are simple to understand.
They naturally support FIFO behavior.
They are useful for scheduling and waiting lines.
Enqueue and dequeue can be O(1).
They are essential for BFS.
They are useful in background job systems and message processing.
These advantages make Queues one of the most important data structures in computer science.
Disadvantages of Queues
Queues also have limitations.
They do not provide fast random access.
Searching inside a Queue is O(n).
They are not suitable for LIFO behavior.
Priority-based processing requires a Priority Queue instead.
Array-based implementations must be designed carefully to avoid inefficient shifting.
These limitations are normal. They simply show that Queues are designed for ordered processing, not every possible access pattern.
Queues in Real Software Projects
Queues appear in real software projects in many forms. Developers may use built-in queues, message brokers, job systems, or framework queue tools instead of manually implementing Queue classes.
Common real-world uses include:
Background job processing.
Email sending.
Notification systems.
Printer job handling.
Request buffering.
Operating system scheduling.
Message queues.
Breadth-first search in trees and graphs.
Understanding Queues helps developers understand how asynchronous and ordered processing systems work internally.
Conclusion
A Queue is a linear data structure that follows the First In, First Out principle. The first element added to the queue is the first element removed.
The main Queue operations are enqueue, dequeue, front, rear, isEmpty, and size. Enqueue adds an element to the rear, while dequeue removes the front element.
With a good implementation, enqueue, dequeue, front, rear, isEmpty, and size are O(1). Searching inside a Queue is O(n) because the structure is not designed for random access.
Queues can be implemented using arrays or linked lists. They also have important variations such as Circular Queue, Priority Queue, and Deque.
For developers learning Data Structures and Algorithms, Queues are essential. They teach FIFO behavior, ordered processing, scheduling logic, BFS traversal, and the foundation of many real-world systems such as background jobs, message queues, and request handling.

