
10 Types of Programming Languages Every Coder should Know
Learn about different types of programming languages and how they are implemented in the real world.

A queue is a linear structure wherein operations occur in a First In First Out (FIFO) order. Queues are data structures in which the head of the queue is removed in the address space where it was added in the order in which it was received. This principle is useful in organising processes among computer components such as task queuing, process scheduling, and handling. Queues are typically used to manage multithreading threads and implement priority queuing systems.
In this article, we will learn about the types of queues, their application, advantages and disadvantages, and various code examples.
A queue is a linear data structure that follows the FIFO (First In First Out) method which is open at both ends. In a Queue, elements are added at one end of the ‘rear’ and removed from the other i.e., from the front. In other words, a queue is a collection where deletions are made from the front end of the line, also known as the head of the queue, and insertions are made from the back end, also known as the tail of the queue.
This can be better understood with a real-world example. Suppose you are waiting in line at a ticket counter. The first person in line will be put at the front of the line, and those who arrive later will join the front person from behind. Services will also be provided in that order, so the first person in line will receive their ticket first. This similar kind of approach is followed in a queue data structure.
Key characteristics of a queue:


POSTGRADUATE PROGRAM IN
Multi Cloud Architecture & DevOps
Master cloud architecture, DevOps practices, and automation to build scalable, resilient systems.
There are mainly 4 (four) types of Queues in data structures. We will now understand each of them in detail, with their implementation and code examples.
The simple queue is the first version of the queue based on the First In First Out (FIFO) principle. Elements are inserted at the rear and removed from the front. However, after performing several insertions and deletions, such a class for the queue can also offer many downsides because of the empty spaces witnessed after some elements are dequeued.
The front end of the queue is the location where the deletion occurs, and the rear end is the location where the insertion occurs. It is used in situations where data consumption should occur in a first-in, first-out (FIFO) principle, but where there may be a requirement to discard recently added data owing to performance concerns or irrelevant data. It should be noted that to remove a newly added element, all items that were added previously must be removed. A simple queue is also known as the Input Restricted Queue.

Implementation
An array or linked list is used to build the Simple Queue (also a Linear Queue). Two important rules to remember are:
Operations of Simple Queue:
Below are the operations of a simple queue in data structure:
Code Example:
C++
#include <iostream>
using namespace std;
#define SIZE 5 // Define the maximum size of the queue
class SimpleQue {
private:
int items[SIZE]; // Array to store queue elements
int front, rear; // Pointers to manage the front and rear of the queue
public:
SimpleQue() {
front = -1; // Initialize front to -1 (empty state)
rear = -1; // Initialize rear to -1 (empty state)
}
// Function to check if the queue is full
bool isFull() {
return rear == SIZE - 1; // Queue is full when rear reaches the last index
}
// Function to check if the queue is empty
bool isEmpty() {
return front == -1; // Queue is empty when front is -1
}
// Enqueue operation to add an element to the queue
void enqueue(int element) {
if (isFull()) {
cout << "Queue is full\n";
} else {
if (front == -1) front = 0; // If inserting the first element, set front to 0
rear++;
items[rear] = element; // Insert element at the rear
cout << "Inserted: " << element << endl;
}
}
// Dequeue operation to remove an element from the queue
int dequeue() {
int element;
if (isEmpty()) {
cout << "Queue is empty\n";
return (-1);
} else {
element = items[front];
if (front >= rear) {
// If only one element was in the queue
front = -1;
rear = -1;
} else {
front++; // Move front pointer forward
}
cout << "Removed: " << element << endl;
return element;
}
}
// Peek operation to see the element at the front without removing it
int peek() {
if (isEmpty()) {
cout << "Queue is empty\n";
return (-1);
} else {
return items[front]; // Return front element
}
}
// Display the queue elements
void display() {
if (isEmpty()) {
cout << "Queue is empty\n";
} else {
cout << "Queue elements: ";
for (int i = front; i <= rear; i++)
cout << items[i] << " ";
cout << endl;
}
}
};
int main() {
SimpleQue q;
q.enqueue(102);
q.enqueue(104);
q.enqueue(106);
q.enqueue(108);
q.display(); // Display all elements
q.dequeue(); // Remove element from the queue
q.display();
q.enqueue(110); // Attempt to enqueue another element
q.display();
cout << "Peek: " << q.peek() << endl; // Peek at the front element
return 0;
}
Output:
Inserted: 102
Inserted: 104
Inserted: 106
Inserted: 108
Queue elements: 102 104 106 108
Removed: 102
Queue elements: 104 106 108
Inserted: 110
Queue elements: 104 106 108 110
Peek: 104
Applications:
A circular queue is an advancement on the simple queue. In a circular queue, all the nodes are connected circularly. Here, the last position is joined to the first position forming a circular shape. This elimination solves the problem of empty spaces found in simple queues after enqueuing and dequeuing several times.
A circular queue is also known as a ring buffer because all of the ends are connected. A circular queue stores its elements in a circular buffer. A circular queue also solves the issue of overflow in a simple queue and offers an effective way to manage memory that is constrained. Circular queues are used in a variety of disciplines, including memory management, CPU scheduling, etc., because of their efficient operation and ability to manage data cyclically.

Implementation
The problem of wasted space in a linear queue is solved by a circular queue data structure. After elements are dequeued, the space is reused using a circular array.
Logic:
Operations of Circular Queue:
Below are the operations of a Circular queue in data structure:
Code Example:
C++
#include <iostream>
using namespace std;
#define SIZE 5 // Define the size of the circular queue
class CircularQue {
private:
int items[SIZE]; // Array to store queue elements
int front, rear; // Front and rear pointers
public:
CircularQue() {
front = -1;
rear = -1;
}
// Check if the queue is full
bool isFull() {
return (front == 0 && rear == SIZE - 1) || (front == rear + 1);
}
// Check if the queue is empty
bool isEmpty() {
return front == -1;
}
// Enqueue operation
void enqueue(int element) {
if (isFull()) {
cout << "Queue is full\n";
} else {
if (front == -1) front = 0; // If first element, set front to 0
rear = (rear + 1) % SIZE; // Circular increment
items[rear] = element;
cout << "Inserted: " << element << endl;
}
}
// Dequeue operation
int dequeue() {
int element;
if (isEmpty()) {
cout << "Queue is empty\n";
return -1;
} else {
element = items[front];
if (front == rear) {
// Queue has only one element, reset queue
front = -1;
rear = -1;
} else {
front = (front + 1) % SIZE; // Circular increment
}
cout << "Removed: " << element << endl;
return element;
}
}
// Peek operation
int peek() {
if (isEmpty()) {
cout << "Queue is empty\n";
return -1;
} else {
return items[front];
}
}
// Display the queue elements
void display() {
if (isEmpty()) {
cout << "Queue is empty\n";
} else {
cout << "Queue elements: ";
int i = front;
while (i != rear) {
cout << items[i] << " ";
i = (i + 1) % SIZE;
}
cout << items[rear] << endl; // Print the last element
}
}
};
int main() {
CircularQue q;
q.enqueue(102);
q.enqueue(104);
q.enqueue(106);
q.enqueue(108);
q.enqueue(110); // Queue becomes full here
q.display(); // Display all elements
q.dequeue(); // Remove one element
q.display();
q.enqueue(120); // Insert a new element
q.display();
return 0;
}
Output:
Inserted: 102
Inserted: 104
Inserted: 106
Inserted: 108
Inserted: 110
Queue elements: 102 104 106 108 110
Removed: 102
Queue elements: 104 106 108 110
Inserted: 120
Queue elements: 104 106 108 110 120
Applications:
A priority queue is a form of queue in which each element is assigned a priority and dequeued based on that priority rather than the order of input. Higher-priority elements are dequeued first, and vice versa. In simpler words, the node with the lowest priority will be the first removed from the queue. The insertion occurs in the order in which the nodes arrive.
This makes it valuable in a variety of real-world applications, including operating system process scheduling, graph theory’s shortest-path algorithms (Dijkstra’s, etc), and event-driven simulations.

Implementation
A binary heap, linked list, or array is used to implement the priority queue. A min-heap or max-heap is the most effective technique (for high-priority pieces at the top).
Priority Management Logic:
Operations of Priority Queue:
Below are the operations of a Priority queue in data structure:
Code Example:
C++
#include <iostream>
#include <vector>
#include <bits/stdc++.h>
using namespace std;
class PriorityQueue {
private:
vector<int> pq; // Dynamic array to store elements
public:
// Insert an element based on priority
void enqueue(int element) {
pq.push_back(element); // Insert the element at the end
sort(pq.begin(), pq.end());
cout << "Inserted: " << element << endl;
}
// Dequeue the max-priority element
int dequeue() {
if (pq.empty()) {
cout << "Queue is empty\n";
return -1;
} else {
int element = pq.front(); // Element with the highest priority
pq.erase(pq.begin()); // Remove the front element
cout << "Removed: " << element << endl;
return element;
}
}
// Peek operation to return the max-priority element without removing it
int peek() {
if (pq.empty()) {
cout << "Queue is empty\n";
return -1;
} else {
return pq.front();
}
}
// Display the queue elements
void display() {
if (pq.empty()) {
cout << "Queue is empty\n";
} else {
cout << "Queue elements: ";
for (int i = 0; i < pq.size(); i++)
cout << pq[i] << " ";
cout << endl;
}
}
};
int main() {
PriorityQueue pq;
pq.enqueue(300);
pq.enqueue(100);
pq.enqueue(200);
pq.display(); // Display all elements (sorted)
pq.dequeue(); // Remove the max-priority element (smallest will be returned)
pq.display();
return 0;
}
Output:
Inserted: 300
Inserted: 100
Inserted: 200
Queue elements: 100 200 300
Removed: 100
Queue elements: 200 300
Applications:
A double-ended queue (Deque) is a type of queue that permits insertion and deletion from both the front and back ends. A deque can support both FIFO (First In First Out) and LIFO (Last In First Out) operations, and therefore we may call this a cross between a stack and a queue. A deque is a flexible data structure that enables effective insertion and deletion at both the front and the end of the queue. This provides more versatility than a standard queue. The Deque can be further divided into two unique queues: input-restricted and output-restricted.

Implementation
The Deque is implemented as a circular array or linked list, allowing insertion and deletion from both the front and the rear. Two important rules to remember are:
Operations of Deque:
Below are the operations of a Deque in data structure:
Code Example:
C++
#include <iostream>
using namespace std;
#define SIZE 5 // Define the size of the deque
class Deque {
private:
int items[SIZE]; // Array to store deque elements
int front, rear; // Pointers for the front and rear of the deque
public:
Deque() {
front = -1;
rear = -1;
}
// Check if the deque is full
bool isFull() {
return (front == 0 && rear == SIZE - 1) || (front == rear + 1);
}
// Check if the deque is empty
bool isEmpty() {
return front == -1;
}
// Insert at the front
void insertFront(int element) {
if (isFull()) {
cout << "Deque is full\n";
} else {
if (front == -1) { // Inserting the first element
front = 0;
rear = 0;
} else if (front == 0) {
front = SIZE - 1; // Wrap around to the end
} else {
front--;
}
items[front] = element;
cout << "Inserted at front: " << element << endl;
}
}
// Insert at the rear
void insertRear(int element) {
if (isFull()) {
cout << "Deque is full\n";
} else {
if (front == -1) { // Inserting the first element
front = 0;
rear = 0;
} else if (rear == SIZE - 1) {
rear = 0; // Wrap around to the front
} else {
rear++;
}
items[rear] = element;
cout << "Inserted at rear: " << element << endl;
}
}
// Remove from the front
int delFront() {
if (isEmpty()) {
cout << "Deque is empty\n";
return -1;
} else {
int element = items[front];
if (front == rear) {
front = -1;
rear = -1; // Reset deque
} else if (front == SIZE - 1) {
front = 0; // Wrap around to the front
} else {
front++;
}
cout << "Removed from front: " << element << endl;
return element;
}
}
// Remove from the rear
int delRear() {
if (isEmpty()) {
cout << "Deque is empty\n";
return -1;
} else {
int element = items[rear];
if (front == rear) {
front = -1;
rear = -1; // Reset deque
} else if (rear == 0) {
rear = SIZE - 1; // Wrap around to the end
} else {
rear--;
}
cout << "Removed from rear: " << element << endl;
return element;
}
}
// Display the deque elements
void display() {
if (isEmpty()) {
cout << "Deque is empty\n";
} else {
cout << "Deque elements: ";
int i = front;
while (i != rear) {
cout << items[i] << " ";
i = (i + 1) % SIZE;
}
cout << items[rear] << endl; // Print the last element
}
}
};
int main() {
Deque dq;
dq.insertRear(10);
dq.insertRear(20);
dq.insertFront(30);
dq.insertFront(40);
dq.display(); // Display all elements
dq.delFront(); // Remove from front
dq.delRear(); // Remove from rear
dq.display();
return 0;
}
Output:
Inserted at rear: 10
Inserted at rear: 20
Inserted at front: 30
Inserted at front: 40
Deque elements: 40 30 10 20
Removed from front: 40
Removed from rear: 20
Deque elements: 30 10
Applications:
One primary advantage of a queue is that it can realise FIFO. That is, the first in time to be added is the first to be removed. This is of importance in actual real-world systems such as scheduling, buffering, and task management.
Queues are used in all task scheduling systems to manage processes. CPU scheduling algorithms such as First-Come-First-Serve use queues to fairly implement efficient process execution.
Queues are perfect when managing shared resources. In networking, queues help in data packet handling to maintain the order of carry and forward.
Queues are most heavily implemented in IPC for buffering. That is if the producer is creating data at a different rate than the consumer can handle, it allows asynchronous data exchange by temporarily storing the message.
Circular queues are an optimization over memory usage. They have no problem with the waste of space, as in the case of a simple linear queue. Once elements have been dequeued from the queue, their positions can be used again in a circular fashion.
In distributed systems or parallel processing, for example, the queuing discipline, queues hold the tasks in them so that these will be guaranteed to be processed in the order of arrival. It helps with load balancing and proper utilisation of system resources.
In array-based implementations where the size is fixed, queues may overflow when they reach capacity. Underflow may also happen when you try to dequeue from an empty queue. The implementation process needs to handle these problems properly.
In contrast to data structures such as arrays or trees, searching for an element in a queue is slow since elements cannot be accessed directly. Dequeuing elements one at a time is necessary to locate an item, which renders the technique ineffective for searches or random access operations.
When several threads or processes are waiting on one another to release resources, a deadlock happens and none of the threads can move forward.
Priority queues, while efficient for tasks that require different priorities for elements, have more complex insertion and deletion operations (especially if implemented with a heap). Maintaining the correct order for priority in every insertion adds extra time and space complexity compared to a basic queue.

82.9%
of professionals don't believe their degree can help them get ahead at work.
Queues are a fundamental concept in data structures, and they are of various types and have different use cases in different scenarios. We have covered majorly 4 types of queues in this article, along with each of their implementation, applications, code implementations, etc. Simple queues are suitable for basic FIFO operations, circular queues address memory wastage, priority queues offer task prioritisation, and deques provide flexibility with double-ended operations.
Understanding these different types of queues, along with their implementation, is crucial for solving real-world problems efficiently. Different applications are there for each type of queue, and having a deep knowledge of each type will help you apply the best queue data structure in your next application.
Updated on October 9, 2024

Learn about different types of programming languages and how they are implemented in the real world.

Explore 10 front-end development, including key languages, its advantages and disadvantages, and how it shapes user experience in web design and functionality.