Queues in DSA: FIFO, Real Use Cases & Implementation
Last modified - 21-08-2026
Author - Krishna Shinde
If you're working through a DSA roadmap or any C++ tutorial series on data structures, the Queue is one you'll meet right after the Stack — and for good reason. Where a stack processes things last-in-first-out, a queue processes them in the exact order they arrived. That single idea shows up everywhere in real software, from task scheduling to how your messages get delivered.
This tutorial covers what a queue is, how it's implemented in C++, why a plain array-based queue has a hidden inefficiency, how the circular queue fixes it, and where queues actually show up in real systems — not just interview questions. As with any data structures topic, the goal is to understand the "why," not just memorize the operations.
Table of Contents
What Is a Queue?
A queue is a linear data structure that follows the FIFO (First In, First Out) principle. The first element added to the queue is the first one removed — exactly like a real-world line at a ticket counter. Whoever joins first gets served first; whoever joins last waits at the back.
Two ends define a queue:
- Front — where elements are removed from
- Rear (or back) — where new elements are added
This is the key difference from a stack, which only ever adds and removes from a single end (the top).
Core Queue Operations
| Operation | What It Does | Time Complexity |
|---|---|---|
| enqueue(x) | Adds element x to the rear of the queue | O(1) |
| dequeue() | Removes and returns the front element | O(1) |
| peek() / front() | Returns the front element without removing it | O(1) |
| isEmpty() | Checks whether the queue has any elements | O(1) |
| isFull() | Checks whether a fixed-size queue has reached capacity | O(1) |
Implementing a Simple Queue in C++ (Array-Based)
Here's a straightforward array-based queue implementation:
#include <iostream>
using namespace std;
class Queue {
int arr[5];
int front, rear;
public:
Queue() {
front = -1;
rear = -1;
}
void enqueue(int val) {
if (rear == 4) {
cout << "Queue is full" << endl;
return;
}
if (front == -1) front = 0;
arr[++rear] = val;
}
void dequeue() {
if (front == -1 || front > rear) {
cout << "Queue is empty" << endl;
return;
}
cout << "Dequeued: " << arr[front++] << endl;
}
};
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
q.enqueue(30);
q.dequeue();
q.dequeue();
return 0;
}Output:
Dequeued: 10
Dequeued: 20
The Hidden Problem With a Simple Queue
This works, but there's a catch. Every dequeue() moves front forward — it never resets. Once rear hits the end of the array, the queue reports "full," even if several slots at the beginning are now empty and unused.
Picture a 5-slot array. You enqueue 5 elements, then dequeue 3. Slots 0, 1, and 2 are free — but rear is still stuck at index 4, so enqueue() refuses to add anything new. The array's front portion is simply wasted space that never gets reused.
This is exactly the problem the circular queue was designed to solve.
Circular Queues in C++
A circular queue treats the underlying array as a ring instead of a straight line. When rear reaches the last index, the next enqueue wraps back around to index 0 (as long as that slot is actually free). This means every slot in the array gets reused instead of being abandoned once passed over.
The wraparound is done with the modulo operator:
rear = (rear + 1) % size;#include <iostream>
using namespace std;
class CircularQueue {
int arr[5];
int front, rear, count, size;
public:
CircularQueue() {
front = 0;
rear = -1;
count = 0;
size = 5;
}
void enqueue(int val) {
if (count == size) {
cout << "Queue is full" << endl;
return;
}
rear = (rear + 1) % size;
arr[rear] = val;
count++;
}
void dequeue() {
if (count == 0) {
cout << "Queue is empty" << endl;
return;
}
cout << "Dequeued: " << arr[front] << endl;
front = (front + 1) % size;
count--;
}
};
int main() {
CircularQueue q;
for (int i = 1; i <= 5; i++) {
q.enqueue(i * 10);
}
q.dequeue();
q.dequeue();
q.enqueue(60);
q.enqueue(70);
return 0;
}Output:
Dequeued: 10
Dequeued: 20Without the circular logic, enqueue(60) and enqueue(70) would have failed with "Queue is full," even though two slots were sitting empty. That single fix is the entire reason circular queues exist.
Other Types of Queues You Should Know
Deque (Double-Ended Queue)
Allows insertion and removal from both the front and the rear. C++'s standard library ships this as std::deque, and it's commonly used to implement both stacks and queues from a single structure.
Priority Queue
Elements are served based on priority rather than arrival order — the highest (or lowest) priority element is dequeued first, regardless of when it was added. C++'s std::priority_queue implements this using a heap internally. It's a separate topic worth its own deep dive.
Queue Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| enqueue() | O(1) | O(1) |
| dequeue() | O(1) | O(1) |
| peek() | O(1) | O(1) |
| Overall queue (n elements) | — | O(n) |
Where Queues Actually Show Up
It's easy to treat queues as just another interview topic, but FIFO ordering is a core building block across real systems:
- CPU & process scheduling — operating systems queue processes waiting for CPU time, serving them in (roughly) arrival order
- Breadth-First Search (BFS) — graph and tree traversal algorithms use a queue to explore nodes level by level; this is the single biggest reason to master queues before graphs
- Print spoolers — documents sent to a shared printer are queued and printed in the order they were submitted
- Message brokers & task queues — systems like RabbitMQ, Kafka, and background job workers are built around queue semantics
- IO buffers — keyboard input buffers and network packet buffers rely on FIFO ordering to avoid dropping or reordering data
- Call center / customer support systems — "your call will be answered in the order it was received" is a queue, literally
Advantages and Disadvantages of Queues
Advantages
- Preserves the exact order in which elements arrive — essential for fairness and correctness in scheduling
- enqueue() and dequeue() are both O(1), so queues stay fast even as they grow
- Simple mental model that maps directly onto real-world processes
Disadvantages
- A naive array-based queue wastes memory unless implemented as circular
- Fixed-size array queues need their capacity decided upfront
- No random access to elements in the middle — you can only interact with the front and rear
Conclusion
The queue is a small idea — first in, first out — but it's foundational to how scheduling, buffering, and traversal work under the hood. Understanding why the circular queue exists, not just how to code one, is what separates memorizing syntax from actually understanding data structures.
Next up: once queues feel solid, they become essential the moment you start Breadth-First Search on trees and graphs — the queue is what makes level-by-level traversal possible. We've already covered stacks in this DSA series; explore the rest of our C++ tutorial and DSA blogs, and follow Algoflame for more programming content.