Queues
Reading time15 minIn brief
Article summary
In this article, we present you a data structure called the “queue”. With this structure, elements are removed in the order in which they are added.
Below, we also propoe two possible implmentations for a queue, and show that they can be used independendly if the primitives abstract the details.
Main takeaways
-
Queues are data structures where the first element to be added is the first element to be removed.
-
Depending on implementation of the queue, some operations may take longer than others.
-
Using primitives allows to hide implementation details.
Article contents
1 — What is a queue?
Like a stack, a queue is a data structure with restricted access. It behaves likes a stack, except that the first element in is the first element out. This access method is called FIFO (First In, First Out). Managing a queue consists of enqueuing an element (adding it to the end of the queue) or dequeuing an element (removing the first element from the queue).
Queues are less common in algorithms than stacks because their scope is less extensive. However, there are areas where they are useful, such as managing the flow of people at a counter, where the first to arrive is the first to be served.
2 — Primitives
In a program using a queue, the implementation details of the data are abstracted away. The program simply queues or dequeues elements from the queue using sub-programs known as “management primitives”. The main primitives for queue operations are:
- initialize – Initializes the queue.
- enqueue – Adds an element to the end of the queue.
- dequeue – Removes the first element from the queue.
- is_empty ** Returns true if the queue is empty, and false otherwise.
Like stacks, queues can be implemented as “arrays” or “linked lists”.
3 — Implementation of queues
Let’s have a look as possible implementations. In practice, each programming language offers a proper implementation of a queue. Here, we show possible implementations for pedagogical purpose.
3.1 — Use of arrays
Managing a queue can be done by adding elements to the end of the array (enqueuing) and simulating the removal of the first element by moving an index that indicates its position (dequeuing). With a fixed-size array, this latter method is less expensive than removing the first element, which involves moving all the other elements. It is also possible to use a dynamic array.
Here is a possible implementation of a queue, using a dynamic size array (list) to store information.
# Needed imports
from typing import Any
from typing_extensions import Self
class Queue:
"""
This is a possible implementation of a queue.
Here, the queue manipulates a list.
Therefore, it is of dynamic size.
"""
def __init__ (self) -> Self:
"""
This is the constructor of the class.
It initializes the queue.
In:
* None.
Out:
* A new queue.
"""
# Initialize data
self.data = []
def __str__ (self) -> str:
"""
This method returns a string representation of the queue.
In:
* None.
Out:
* A string representation of the queue.
"""
# Create string
return str(self.data)
def enqueue (self, item: Any) -> None:
"""
This method adds an element to the queue.
The element is added at the end.
In:
* item: Element to add.
Out:
* None.
"""
# Add element
self.data.append(item)
def dequeue (self) -> Any:
"""
This method first the last element of the queue.
The element is removed from the beginning.
In:
* None.
Out:
* The first element of the queue.
"""
# Remove element
return self.data.pop(0)
def is_empty (self) -> bool:
"""
This method checks if the queue is empty.
In:
* None.
Out:
* True if the queue is empty, False otherwise.
"""
# Check contents
return len(self.data) == 0
# A few tests
queue = Queue()
queue.enqueue(1)
print(queue)
queue.enqueue(2)
print(queue)
queue.enqueue(3)
print(queue)
element = queue.dequeue()
print(element, queue)
element = queue.dequeue()
print(element, queue)
element = queue.dequeue()
print(element, queue)
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Queue.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Queue.java` to compile the code.
* - Run the command `java -ea Queue` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
// Needed imports
import java.util.ArrayList;
import java.util.List;
/**
* This is a possible implementation of a queue.
* Here, the stack manipulates a dynamically sized ArrayList.
* This class should be put in a file named 'Queue.java'.
*/
public class Queue<T> {
/**
* Here is the attribute we need to define for data.
*/
private List<T> data;
/**
* This is the constructor of the class.
* It initializes the empty queue.
*/
public Queue() {
// Initialize data
this.data = new ArrayList<>();
}
/**
* This method returns a string representation of the queue.
*
* @return A string representation of the queue.
*/
public String toString() {
// Create string
StringBuilder string = new StringBuilder("[");
for (T d : data) {
string.append(d).append(", ");
}
if (!data.isEmpty()) {
string.setLength(string.length() - 2);
}
string.append("]");
return string.toString();
}
/**
* This method adds an element to the queue.
* The element is added at the end.
*
* @param item Element to add.
*/
public void enqueue(T item) {
// Add element
data.add(item);
}
/**
* This method removes the first element of the queue.
*
* @return The first element of the stack.
*/
public T dequeue() {
// Make sure the stack is not empty
if (isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
// Remove element
return data.remove(0);
}
/**
* This method checks if the queue is empty.
*
* @return True if the queue is empty, False otherwise.
*/
public boolean isEmpty() {
// Check data size
return data.isEmpty();
}
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main(String[] args) {
// A few tests
Queue<Integer> queue = new Queue<>();
queue.enqueue(1);
System.out.println(queue);
queue.enqueue(2);
System.out.println(queue);
queue.enqueue(3);
System.out.println(queue);
int element = queue.dequeue();
System.out.println(element + " " + queue);
element = queue.dequeue();
System.out.println(element + " " + queue);
element = queue.dequeue();
System.out.println(element + " " + queue);
}
}
3.2 — Use of linked lists
Managing a queue with a linked list is similar to managing a stack. The type element is unchanged. Enqueuing creates a new structure which is added to the end of the list, as with a stack. Most of the primitives are identical to those used to manage the stack.
Here is a possible implementation with a double linked list:
# Needed imports
from typing import Any
from typing_extensions import Self
class Node:
"""
This class represents a node in a doubly linked list.
Each node contains data, a reference to the next node, and a reference to the previous node.
"""
def __init__(self, data: Any):
"""
This is the constructor of the class.
It initializes a node with the given data.
In:
* data: Data to store in the node.
Out:
* None.
"""
# Here we initialize the node with the given data
self.data = data
self.next = None
self.prev = None
class Queue:
"""
This is a possible implementation of a queue using a doubly linked list.
The queue can grow dynamically as elements are added.
"""
def __init__ (self) -> Self:
"""
This is the constructor of the class.
It initializes an empty queue using a doubly linked list.
In:
* None.
Out:
* A new queue.
"""
# Here we initialize the queue with no elements
self.front = None
self.rear = None
self.nb_elements = 0
def __str__ (self) -> str:
"""
This method returns a string representation of the queue.
In:
* None.
Out:
* A string representation of the queue.
"""
# Create string
string = "["
current = self.front
while current:
string += str(current.data) + ", "
current = current.next
if self.nb_elements > 0:
string = string[:-2]
string += "]"
return string
def enqueue (self, item: Any) -> None:
"""
This method adds an element to the queue.
The element is added at the rear, and the counter is incremented.
In:
* item: Element to add.
Out:
* None.
"""
# Create node
new_node = Node(item)
# If the queue is empty, set both front and rear to the new node
if self.rear is None:
self.front = self.rear = new_node
else:
self.rear.next = new_node
new_node.prev = self.rear
self.rear = new_node
# Increment counter
self.nb_elements += 1
def dequeue (self) -> Any:
"""
This method removes the front element of the queue.
The element is removed and the counter is decremented.
In:
* None.
Out:
* The front element of the queue.
"""
# Make sure the queue is not empty
assert not self.is_empty()
# Remove the front element
element = self.front.data
self.front = self.front.next
if self.front is not None:
self.front.prev = None
else:
self.rear = None
# Decrease counter
self.nb_elements -= 1
return element
def is_empty (self) -> bool:
"""
This method checks if the queue is empty.
In:
* None.
Out:
* True if the queue is empty, False otherwise.
"""
# Check counter
return self.nb_elements == 0
# A few tests
queue = Queue()
queue.enqueue(1)
print(queue)
queue.enqueue(2)
print(queue)
queue.enqueue(3)
print(queue)
element = queue.dequeue()
print(element, queue)
element = queue.dequeue()
print(element, queue)
element = queue.dequeue()
print(element, queue)
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
// Needed imports
/**
* This class represents a node in a doubly linked list.
* Each node contains data, a reference to the next node, and a reference to the previous node.
* This class should be put in a file named 'Node.java'.
*/
class Node<T> {
/**
* Here are the attributes we need to define for data and the number of elements.
*/
T data;
Node<T> next;
Node<T> prev;
/**
* This is the constructor of the class.
* It initializes a node with the given data.
*
* @param data Data to store in the node.
*/
public Node(T data) {
// Here we initialize the node with the given data
this.data = data;
this.next = null;
this.prev = null;
}
}
/**
* This is a possible implementation of a queue using a doubly linked list.
* The queue can grow dynamically as elements are added.
* This class should be put in a file named 'Queue.java'.
*/
public class Queue<T> {
/**
* Here are the attributes we need to define for the front, rear, and the number of elements.
*/
private Node<T> front;
private Node<T> rear;
private int nbElements;
/**
* This is the constructor of the class.
* It initializes an empty queue using a doubly linked list.
*/
public Queue() {
// Here we initialize the queue with no elements
this.front = null;
this.rear = null;
this.nbElements = 0;
}
/**
* This method returns a string representation of the queue.
*
* @return A string representation of the queue.
*/
public String toString() {
// Create string
StringBuilder string = new StringBuilder("[");
Node<T> current = front;
while (current != null) {
string.append(current.data).append(", ");
current = current.next;
}
if (nbElements > 0) {
string.setLength(string.length() - 2);
}
string.append("]");
return string.toString();
}
/**
* This method adds an element to the queue.
* The element is added at the rear, and the counter is incremented.
*
* @param item Element to add.
*/
public void enqueue(T item) {
// Create a new node with the item
Node<T> newNode = new Node<>(item);
// Link the new node with the current rear node
if (rear != null) {
rear.next = newNode;
newNode.prev = rear;
}
// Set the new node as the rear node
rear = newNode;
// If the queue was empty, set the front to the new node
if (front == null) {
front = newNode;
}
// Increment the number of elements
nbElements++;
}
/**
* This method removes the front element of the queue.
* The element is removed and the counter is decremented.
*
* @return The front element of the queue.
*/
public T dequeue() {
// Make sure the queue is not empty
if (isEmpty()) {
throw new IllegalStateException("Queue is empty");
}
// Remove the front element
T element = front.data;
front = front.next;
if (front != null) {
front.prev = null;
} else {
rear = null;
}
// Decrement the number of elements
nbElements--;
return element;
}
/**
* This method checks if the queue is empty.
*
* @return True if the queue is empty, False otherwise.
*/
public boolean isEmpty() {
// Check if the queue is empty
return nbElements == 0;
}
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main(String[] args) {
// A few tests
Queue<Integer> queue = new Queue<>();
queue.enqueue(1);
System.out.println(queue);
queue.enqueue(2);
System.out.println(queue);
queue.enqueue(3);
System.out.println(queue);
int element = queue.dequeue();
System.out.println(element + " " + queue);
element = queue.dequeue();
System.out.println(element + " " + queue);
element = queue.dequeue();
System.out.println(element + " " + queue);
}
}
4 — A practical use
The following is an example of a program that uses a queue to process a string.
# Initialize the queue
queue = Queue()
# Add elements inside
sentence = (input("Enter a name of a city (e.g., Brest): "))
for c in sentence:
queue.enqueue(c)
print("Added:", c, end=" ")
print("==> Queue:", queue)
# Remove elements from the queue
while not queue.is_empty() > 0:
print("Delete:", queue.dequeue(), end=" ")
print("==> Queue:", queue)
// Needed imports
import java.util.Scanner;
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
public class Main
{
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main (String[] args)
{
// Initialize the queue
Queue<Character> queue = new Queue<>();
// Read input from user
Scanner scanner = new Scanner(System.in);
System.out.print("Enter a name of a city (e.g., Brest): ");
String sentence = scanner.nextLine();
scanner.close();
// Add elements inside
for (char c : sentence.toCharArray())
{
queue.enqueue(c);
System.out.println("Added: " + c + " ==> Queue: " + queue);
}
// Remove elements from the queue
while (!queue.isEmpty())
{
System.out.println("Delete: " + queue.dequeue() + " ==> Queue: " + queue);
}
}
}
Remember that all examples above are pedagogical, and made to understand how to create structures.
For such simple structures, you should use default implementations.
For instance, it is more appropriate to use the Python deque
class in package collections
, which provides the optimal operations for enqueuing and dequeuing.
To go further
The content of this section is optional. It contains additional material for you to consolidate your understanding of the current topic.
To go beyond
The content of this section is very optional. We suggest you directions to explore if you wish to go deeper in the current topic.
- name.
Short description.