Queues are a fundamental data structure in computer science that follows the FIFO principle FIFO — First In, First Out.
That means the first element that goes into the queue is also the first one to come out.
This simple rule makes queues extremely powerful, and you’ll find them everywhere in real-world systems like:
And beyond that, queues are also an important topic for coding interviews. Many tricky problems become much simpler once you recognize that a queue is the right data structure to use.
In this chapter, we’ll cover:
Lets get started:
So, what exactly is a queue?
A queue is a linear data structure that processes elements in the exact order they arrive — following the classic FIFO principle: First In, First Out.
That means:
A simple way to think about queues is people standing in line at a movie theater:
Now that you understand what a queue is, lets walk through the common queue operations you will use with a simple example.
This operation adds an element to the back of the queue.
Starting with an empty queue.
We enqueue 3 values one by one:
Enqueue(10):
Enqueue(20):
Enqueue(30):
This removes the front-most element from the queue.
For our example, after Dequeue(), we remove 10 since it was the first element that got added:
Dequeue():
This lets you look at the front element without removing it.
Calling Peek() on our current example gives you 20, but the queue stays unchanged.
and finally isEmpty checks whether the queue contains any elements.
For our current queue example, it will return false but if we dequeue both 20 and 30, isEmpty() will return true.
These operations are all constant time making queues a very efficient data structure for many real-world systems.
Now that you understand what a queue is and how it works, let’s talk about how to actually implement one.
There are multiple ways to implement a queue. Let’s explore the two most common approaches:
This is the simplest and most intuitive way to build a queue.
rear).front).But there’s a problem.
If you use a simple array, removing the front element means shifting all the other elements one position to the left. That takes O(n) time — which is inefficient for large queues.
To avoid shifting, we use a circular array (or circular buffer).
Here’s how we can implement this in code:
front and rear — these track the start and end of the queue.rear pointer forward, wrapping it around using % capacity if needed, and insert the new element at that position.front, then move the front pointer forward, again wrapping it around circularly.This circular movement ensures that we can reuse slots freed up by dequeue operations — preventing wasted space at the beginning of the array.
By wrapping around both front and rear using % capacity, we keep the queue compact and efficient, without needing to shift elements.
Another popular and efficient way to implement a queue is using a linked list.
A linked list grows and shrinks dynamically, with no need to shift elements. You get O(1) time for both enqueue and dequeue operations without the complexity of circular indexing.
Here’s how it works:
We maintain two pointers:
head: points to the front of the queue (used for dequeue)tail: points to the rear of the queue (used for enqueue)tail to point to the newly added node. If the queue was empty (i.e., both head and tail were null), we also set head = newNode. This ensures that the first element becomes both the head and tail.head is null, the queue is empty so we throw and error. We extract the value from the front node. We update head to point to the next node — removing the front element. If the queue becomes empty after removal (i.e., head becomes null), we also set tail = null. This prevents stale pointers and keeps the structure clean. Finally, return the dequeued value.In most coding interviews — and almost all real-world projects — you don’t need to build a queue from scratch.
Because modern programming languages already provide efficient, well-tested, and optimized queue implementations in their standard libraries.
Java provides a Queue interface, with popular implementations like LinkedList and ArrayDeque.
Python doesn't have a built-in Queue class in the core language. But it does have deque in the collections module and it works perfectly for queues.
In C++, the Standard Template Library (STL) provides a queue container adapter built on top of other containers like deque or list.
These libraries are optimized and ready to use, so unless the interviewer specifically asks you to implement a queue from scratch, it’s best to rely on them.
So far, we've covered the basic idea of a queue — a First In, First Out (FIFO) data structure.
But there are actually several variations of queues, each designed for different use cases.
Let’s go through the most important ones.
This is the most basic form of a queue and we have already covered it in detail.
However, when implemented using a simple array, it can lead to wasted space. After several dequeues, the front keeps moving forward, leaving unused slots at the beginning of the array.
To fix this, we use a smarter version — the circular queue.
A circular queue eliminates the wasted-space problem by treating the array as circular.
Circular queues are especially useful in memory-constrained environments, like buffering systems or resource scheduling.
A deque, short for double-ended queue is a more flexible version of a queue that allows insertion and deletion from both ends:
This makes deques extremely versatile.
A priority queue doesn’t follow the traditional FIFO order. Instead:
A classic real-world example is CPU scheduling in operating systems:
The OS maintains a queue of tasks, each with a priority level. Tasks with higher priority are executed before those with lower priority.