Queue is a linear data structure that follows FIFO (First In First Out) Principle, so the first element inserted is the first to be popped out. In this article, we will cover all the basics of Queue, Operations on Queue, its implementation, advantages, disadvantages which will help you solve all the problems based on Queue.
What is Queue?
Queue is a linear data structure that is open at both ends and the operations are performed in First In First Out (FIFO) order.
We define a queue to be a list in which all additions to the list are made at one end (back of the queue), and all deletions from the list are made at the other end(front of the queue). The element which is first pushed into the order, the delete operation is first performed on that.
FIFO Principle of Queue:
- A Queue is like a line waiting to purchase tickets, where the first person in line is the first person served. (i.e. First Come First Serve).
- Position of the entry in a queue ready to be served, that is, the first entry that will be removed from the queue, is called the front of the queue(sometimes, head of the queue). Similarly, the position of the last entry in the queue, that is, the one most recently added, is called the rear (or the tail) of the queue.
Representation of Queue Data Structure:
The image below shows how we represent Queue Data Structure:
Types of Queue:
Queue data structure can be classified into 4 types:
There are different types of queues:
- Simple Queue: Simple Queue simply follows FIFO Structure. We can only insert the element at the back and remove the element from the front of the queue.
- Double-Ended Queue (Dequeue): In a double-ended queue the insertion and deletion operations, both can be performed from both ends. They are of two types:
- Input Restricted Queue: This is a simple queue. In this type of queue, the input can be taken from only one end but deletion can be done from any of the ends.
- Output Restricted Queue: This is also a simple queue. In this type of queue, the input can be taken from both ends but deletion can be done from only one end.
- Circular Queue: This is a special type of queue where the last position is connected back to the first position. Here also the operations are performed in FIFO order.
- Priority Queue: A priority queue is a special queue where the elements are accessed based on the priority assigned to them. They are of two types:
- Ascending Priority Queue: In Ascending Priority Queue, the elements are arranged in increasing order of their priority values. Element with smallest priority value is popped first.
- Descending Priority Queue: In Descending Priority Queue, the elements are arranged in decreasing order of their priority values. Element with largest priority is popper first.
Basic Operations on Queue:
Some of the basic operations for Queue in Data Structure are:
- Enqueue: Adds (or stores) an element to the end of the queue..
- Dequeue: Removal of elements from the queue.
- Peek or front: Acquires the data element available at the front node of the queue without deleting it.
- rear: This operation returns the element at the rear end without removing it.
- isFull: Validates if the queue is full.
- isEmpty: Checks if the queue is empty.
There are a few supporting operations (auxiliary operations):
Enqueue Operation in Queue:
Enqueue() operation in Queue adds (or stores) an element to the end of the queue.
The following steps should be taken to enqueue (insert) data into a queue:
- Step 1: Check if the queue is full.
- Step 2: If the queue is full, return overflow error and exit.
- Step 3: If the queue is not full, increment the rear pointer to point to the next empty space.
- Step 4: Add the data element to the queue location, where the rear is pointing.
- Step 5: return success.
Implementation of Enqueue:
void queueEnqueue(int data)
{
// Check queue is full or not
if (capacity == rear) {
printf("\nQueue is full\n");
return;
}
// Insert element at the rear
else {
queue[rear] = data;
rear++;
}
return;
}
// Function to add an item to the queue.
// It changes rear and size
void enqueue(struct Queue* queue, int item)
{
if (isFull(queue))
return;
queue->rear = (queue->rear + 1) % queue->capacity;
queue->array[queue->rear] = item;
queue->size = queue->size + 1;
printf("%d enqueued to queue\n", item);
}
// This code is contributed by Susobhan Akhuli
void queueEnqueue(int data)
{
// Check queue is full or not
if (capacity == rear) {
System.out.println("\nQueue is full\n");
return;
}
// Insert element at the rear
else {
queue[rear] = data;
rear++;
}
return;
}
// This code is contributed by aadityapburujwale
# Function to add an item to the queue.
# It changes rear and size
def EnQueue(self, item):
if self.isFull():
print("Full")
return
self.rear = (self.rear + 1) % (self.capacity)
self.Q[self.rear] = item
self.size = self.size + 1
print("% s enqueued to queue" % str(item))
# This code is contributed by Susobhan Akhuli
// Function to add an item to the queue.
// It changes rear and size
public void enqueue(int item)
{
if (rear == max - 1) {
Console.WriteLine("Queue Overflow");
return;
}
else {
ele[++rear] = item;
}
}
// This code is contributed by Susobhan Akhuli
<script>
enqueue(element){
// adding element to the queue
this.items.push(element);
}
// This code is contributed by Susobhan Akhuli
</script>
Dequeue Operation in Queue:
Removes (or access) the first element from the queue.
The following steps are taken to perform the dequeue operation:
- Step 1: Check if the queue is empty.
- Step 2: If the queue is empty, return the underflow error and exit.
- Step 3: If the queue is not empty, access the data where the front is pointing.
- Step 4: Increment the front pointer to point to the next available data element.
- Step 5: The Return success.
Implementation of dequeue:
void queueDequeue()
{
// If queue is empty
if (front == rear) {
printf("\nQueue is empty\n");
return;
}
// Shift all the elements from index 2
// till rear to the left by one
else {
for (int i = 0; i < rear - 1; i++) {
queue[i] = queue[i + 1];
}
// decrement rear
rear--;
}
return;
}
// Function to remove an item from queue.
// It changes front and size
int dequeue(struct Queue* queue)
{
if (isEmpty(queue)) {
printf("\nQueue is empty\n");
return;
}
int item = queue->array[queue->front];
queue->front = (queue->front + 1) % queue->capacity;
queue->size = queue->size - 1;
return item;
}
// This code is contributed by Susobhan Akhuli
void queueDequeue()
{
// If queue is empty
if (front == rear) {
System.out.println("\nQueue is empty\n");
return;
}
// Shift all the elements from index 2
// till rear to the left by one
else {
for (int i = 0; i < rear - 1; i++) {
queue[i] = queue[i + 1];
}
// decrement rear
rear--;
}
return;
}
// This code is contributed by aadityapburujwale
# Function to remove an item from queue.
# It changes front and size
def DeQueue(self):
if self.isEmpty():
print("Queue is empty")
return
print("% s dequeued from queue" % str(self.Q[self.front]))
self.front = (self.front + 1) % (self.capacity)
self.size = self.size - 1
# This code is contributed by Susobhan Akhuli
// Function to remove an item from queue.
// It changes front and size
public int dequeue()
{
if (front == rear + 1) {
Console.WriteLine("Queue is Empty");
return -1;
}
else {
int p = ele[front++];
return p;
}
}
// This code is contributed by Susobhan Akhuli
<script>
dequeue(){
// removing element from the queue
// returns underflow when called
// on empty queue
if(this.isEmpty()){
document.write("<br>Queue is empty<br>");
return -1;
}
return this.items.shift();
}
// This code is contributed by Susobhan Akhuli
</script>
Front Operation in Queue:
This operation returns the element at the front end without removing it.
// Function to get front of queue
int front(Queue* queue)
{
if (isempty(queue))
return INT_MIN;
return queue->arr[queue->front];
}
// Function to get front of queue
int front(struct Queue* queue)
{
if (isempty(queue))
return INT_MIN;
return queue->arr[queue->front];
}
// This code is contributed by Susobhan Akhuli
// Function to get front of queue
int front(Queue queue)
{
if (isempty(queue))
return Integer.MIN_VALUE;
return queue.arr[queue.front];
}
// This code is contributed by aadityapburujwale
# Function to get front of queue
def que_front(self):
if self.isempty():
return "Queue is empty"
return self.Q[self.front]
# This code is contributed By Susobhan Akhuli
// Function to get front of queue
public int front()
{
if (isempty())
return INT_MIN;
return arr[front];
}
// This code is contributed By Susobhan Akhuli
<script>
// Function to get front of queue
front(){
// returns the Front element of
// the queue without removing it.
if(this.isEmpty())
return "No elements in Queue<br>";
return this.items[0];
}
// This code is contributed By Susobhan Akhuli
<script>
Rear Operation in Queue:
This operation returns the element at the rear end without removing it.
int rear(queue<int>& myQueue)
{
queue<int> tempQueue = myQueue;
while (tempQueue.size() > 1) {
tempQueue.pop();
}
return tempQueue.front();
}
int rear(struct Queue* front)
{
if (front == NULL) {
printf("Queue is empty.\n");
return -1;
}
while (front->next != NULL) {
front = front->next;
}
return front->data;
}
public static int rear(Queue<Integer> myQueue)
{
if (myQueue.isEmpty()) {
System.out.println("Queue is empty.");
return -1;
}
int rearElement = -1;
while (!myQueue.isEmpty()) {
rearElement = myQueue.poll();
}
return rearElement;
}
def rear(queue):
if queue.empty():
print("Queue is empty.")
return None
rear_element = None
while not queue.empty():
rear_element = queue.get()
return rear_element
static int Rear(Queue<int> myQueue)
{
if (myQueue.Count == 0) {
Console.WriteLine("Queue is empty.");
return -1;
}
int rearElement = -1;
while (myQueue.Count > 0) {
rearElement = myQueue.Dequeue();
}
return rearElement;
}
function rear(queue) {
if (queue.length === 0) {
console.log("Queue is empty.");
return -1;
}
let rearElement = -1;
while (queue.length > 0) {
rearElement = queue.shift();
}
return rearElement;
}
isEmpty Operation in Queue:
This operation returns a boolean value that indicates whether the queue is empty or not.
// This function will check whether
// the queue is empty or not:
bool isEmpty()
{
if (front == -1)
return true;
else
return false;
}
// Queue is empty when size is 0
bool isEmpty(struct Queue* queue)
{
return (queue->size == 0);
}
// This code is contributed by Susobhan Akhuli
// This function will check whether
// the queue is empty or not:
boolean isEmpty()
{
if (front == -1)
return true;
else
return false;
}
// This code is contributed by aadityapburujwale
# Queue is empty when size is 0
def isEmpty(self):
return self.size == 0
# This code is contributed by Susobhan Akhuli
// This function will check whether
// the queue is empty or not:
bool isEmpty()
{
if (front == -1)
return true;
else
return false;
}
// This code is contributed by lokeshmvs21.
</script>
isEmpty(){
// return true if the queue is empty.
return this.items.length == 0;
}
// This code is contributed by Susobhan Akhuli
</script>
isFull Operation in Queue:
This operation returns a boolean value that indicates whether the queue is full or not.
// This function will check
// whether the queue is full or not.
bool isFull()
{
if (front == 0 && rear == MAX_SIZE - 1) {
return true;
}
return false;
}
// Queue is full when size becomes
// equal to the capacity
bool isFull(struct Queue* queue)
{
return (queue->size == queue->capacity);
}
// This code is contributed by Susobhan Akhuli
// This function will check
// whether the queue is full or not.
boolean isFull()
{
if (front == 0 && rear == MAX_SIZE - 1) {
return true;
}
return false;
}
// This code is contributed by aadityapburujwale
# Queue is full when size becomes
# equal to the capacity
def isFull(self):
return self.size == self.capacity
# This code is contributed by Susobhan Akhuli
// Function to add an item to the queue.
// It changes rear and size
public bool isFull(int item) { return (rear == max - 1); }
// This code is contributed by Susobhan Akhuli
// This function will check
// whether the queue is full or not.
function isFull(){
if(front==0 && rear==MAX_SIZE-1){
return true;
}
return false;
}
Queue Implementation:
Queue can be implemented using following data structures:
We have discussed the implementation of Queue below:
// Implementation of queue(enqueue, dequeue).
#include <bits/stdc++.h>
using namespace std;
class Queue {
public:
int front, rear, size;
unsigned cap;
int* arr;
};
Queue* createQueue(unsigned cap)
{
Queue* queue = new Queue();
queue->cap = cap;
queue->front = queue->size = 0;
queue->rear = cap - 1;
queue->arr = new int[(queue->cap * sizeof(int))];
return queue;
}
int isFull(Queue* queue)
{
return (queue->size == queue->cap);
}
int isempty(Queue* queue) { return (queue->size == 0); }
// Function to add an item to the queue.
// It changes rear and size.
void enqueue(Queue* queue, int item)
{
if (isFull(queue))
return;
queue->rear = (queue->rear + 1) % queue->cap;
queue->arr[queue->rear] = item;
queue->size = queue->size + 1;
cout << item << " enqueued to queue\n";
}
// Function to remove an item from queue.
// It changes front and size
int dequeue(Queue* queue)
{
if (isempty(queue))
return INT_MIN;
int item = queue->arr[queue->front];
queue->front = (queue->front + 1) % queue->cap;
queue->size = queue->size - 1;
return item;
}
int front(Queue* queue)
{
if (isempty(queue))
return INT_MIN;
return queue->arr[queue->front];
}
int rear(Queue* queue)
{
if (isempty(queue))
return INT_MIN;
return queue->arr[queue->rear];
}
// Driver code
int main()
{
Queue* queue = createQueue(1000);
enqueue(queue, 10);
enqueue(queue, 20);
enqueue(queue, 30);
enqueue(queue, 40);
cout << dequeue(queue);
cout << " dequeued from queue\n";
cout << "Front item is " << front(queue) << endl;
cout << "Rear item is " << rear(queue);
return 0;
}
// C program for array implementation of queue
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
// A structure to represent a queue
struct Queue {
int front, rear, size;
unsigned capacity;
int* array;
};
// function to create a queue
// of given capacity.
// It initializes size of queue as 0
struct Queue* createQueue(unsigned capacity)
{
struct Queue* queue
= (struct Queue*)malloc(sizeof(struct Queue));
queue->capacity = capacity;
queue->front = queue->size = 0;
// This is important, see the enqueue
queue->rear = capacity - 1;
queue->array
= (int*)malloc(queue->capacity * sizeof(int));
return queue;
}
// Queue is full when size becomes
// equal to the capacity
int isFull(struct Queue* queue)
{
return (queue->size == queue->capacity);
}
// Queue is empty when size is 0
int isEmpty(struct Queue* queue)
{
return (queue->size == 0);
}
// Function to add an item to the queue.
// It changes rear and size
void enqueue(struct Queue* queue, int item)
{
if (isFull(queue))
return;
queue->rear = (queue->rear + 1) % queue->capacity;
queue->array[queue->rear] = item;
queue->size = queue->size + 1;
printf("%d enqueued to queue\n", item);
}
// Function to remove an item from queue.
// It changes front and size
int dequeue(struct Queue* queue)
{
if (isEmpty(queue))
return INT_MIN;
int item = queue->array[queue->front];
queue->front = (queue->front + 1) % queue->capacity;
queue->size = queue->size - 1;
return item;
}
// Function to get front of queue
int front(struct Queue* queue)
{
if (isEmpty(queue))
return INT_MIN;
return queue->array[queue->front];
}
// Function to get rear of queue
int rear(struct Queue* queue)
{
if (isEmpty(queue))
return INT_MIN;
return queue->array[queue->rear];
}
// Driver program to test above functions./
int main()
{
struct Queue* queue = createQueue(1000);
enqueue(queue, 10);
enqueue(queue, 20);
enqueue(queue, 30);
enqueue(queue, 40);
printf("%d dequeued from queue\n", dequeue(queue));
printf("Front item is %d\n", front(queue));
printf("Rear item is %d\n", rear(queue));
return 0;
}
// This code is contributed by Susobhan Akhuli
// Java program for array
// implementation of queue
// A class to represent a queue
class Queue {
int front, rear, size;
int capacity;
int array[];
public Queue(int capacity)
{
this.capacity = capacity;
front = this.size = 0;
rear = capacity - 1;
array = new int[this.capacity];
}
// Queue is full when size becomes
// equal to the capacity
boolean isFull(Queue queue)
{
return (queue.size == queue.capacity);
}
// Queue is empty when size is 0
boolean isEmpty(Queue queue)
{
return (queue.size == 0);
}
// Method to add an item to the queue.
// It changes rear and size
void enqueue(int item)
{
if (isFull(this))
return;
this.rear = (this.rear + 1) % this.capacity;
this.array[this.rear] = item;
this.size = this.size + 1;
System.out.println(item + " enqueued to queue");
}
// Method to remove an item from queue.
// It changes front and size
int dequeue()
{
if (isEmpty(this))
return Integer.MIN_VALUE;
int item = this.array[this.front];
this.front = (this.front + 1) % this.capacity;
this.size = this.size - 1;
return item;
}
// Method to get front of queue
int front()
{
if (isEmpty(this))
return Integer.MIN_VALUE;
return this.array[this.front];
}
// Method to get rear of queue
int rear()
{
if (isEmpty(this))
return Integer.MIN_VALUE;
return this.array[this.rear];
}
}
// Driver class
public class Test {
public static void main(String[] args)
{
Queue queue = new Queue(1000);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
System.out.println(queue.dequeue()
+ " dequeued from queue");
System.out.println("Front item is "
+ queue.front());
System.out.println("Rear item is " + queue.rear());
}
}
// This code is contributed by Susobhan Akhuli
# Python3 program for array implementation of queue
# Class Queue to represent a queue
class Queue:
# __init__ function
def __init__(self, capacity):
self.front = self.size = 0
self.rear = capacity - 1
self.Q = [None]*capacity
self.capacity = capacity
# Queue is full when size becomes
# equal to the capacity
def isFull(self):
return self.size == self.capacity
# Queue is empty when size is 0
def isEmpty(self):
return self.size == 0
# Function to add an item to the queue.
# It changes rear and size
def EnQueue(self, item):
if self.isFull():
print("Full")
return
self.rear = (self.rear + 1) % (self.capacity)
self.Q[self.rear] = item
self.size = self.size + 1
print("% s enqueued to queue" % str(item))
# Function to remove an item from queue.
# It changes front and size
def DeQueue(self):
if self.isEmpty():
print("Empty")
return
print("% s dequeued from queue" % str(self.Q[self.front]))
self.front = (self.front + 1) % (self.capacity)
self.size = self.size - 1
# Function to get front of queue
def que_front(self):
if self.isEmpty():
print("Queue is empty")
print("Front item is", self.Q[self.front])
# Function to get rear of queue
def que_rear(self):
if self.isEmpty():
print("Queue is empty")
print("Rear item is", self.Q[self.rear])
# Driver Code
if __name__ == '__main__':
queue = Queue(30)
queue.EnQueue(10)
queue.EnQueue(20)
queue.EnQueue(30)
queue.EnQueue(40)
queue.DeQueue()
queue.que_front()
queue.que_rear()
# This code is contributed by Susobhan Akhuli
// C# program for array implementation of queue
using System;
namespace GeeksForGeeks {
// A class to represent a linearqueue
class Queue {
private int[] ele;
private int front;
private int rear;
private int max;
public Queue(int size)
{
ele = new int[size];
front = 0;
rear = -1;
max = size;
}
// Function to add an item to the queue.
// It changes rear and size
public void enqueue(int item)
{
if (rear == max - 1) {
Console.WriteLine("Queue Overflow");
return;
}
else {
ele[++rear] = item;
}
}
// Function to remove an item from queue.
// It changes front and size
public int dequeue()
{
if (front == rear + 1) {
Console.WriteLine("Queue is Empty");
return -1;
}
else {
Console.WriteLine(ele[front]
+ " dequeued from queue");
int p = ele[front++];
Console.WriteLine("Front item is {0}",
ele[front]);
Console.WriteLine("Rear item is {0} ",
ele[rear]);
return p;
}
}
// Function to print queue.
public void printQueue()
{
if (front == rear + 1) {
Console.WriteLine("Queue is Empty");
return;
}
else {
for (int i = front; i <= rear; i++) {
Console.WriteLine(ele[i]
+ " enqueued to queue");
}
}
}
}
// Driver code
class Program {
static void Main()
{
Queue Q = new Queue(5);
Q.enqueue(10);
Q.enqueue(20);
Q.enqueue(30);
Q.enqueue(40);
Q.printQueue();
Q.dequeue();
}
}
}
// This code is contributed by Susobhan Akhuli
<script>
// Queue class
class Queue
{
// Array is used to implement a Queue
constructor()
{
this.items = [];
}
isEmpty()
{
// return true if the queue is empty.
return this.items.length == 0;
}
enqueue(element)
{
// adding element to the queue
this.items.push(element);
document.write(element + " enqueued to queue<br>");
}
dequeue()
{
// removing element from the queue
// returns underflow when called
// on empty queue
if(this.isEmpty())
return "Underflow<br>";
return this.items.shift();
}
front()
{
// returns the Front element of
// the queue without removing it.
if(this.isEmpty())
return "No elements in Queue<br>";
return this.items[0];
}
rear()
{
// returns the Rear element of
// the queue without removing it.
if(this.isEmpty())
return "No elements in Queue<br>";
return this.items[this.items.length-1];
}
}
// creating object for queue class
var queue = new Queue();
// Adding elements to the queue
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
// queue contains [10, 20, 30, 40]
// removes 10
document.write(queue.dequeue() + " dequeued from queue<br>");
// queue contains [20, 30, 40]
// Front is now 20
document.write("Front item is " + queue.front() + "<br>");
// printing the rear element
// Rear is 40
document.write("Rear item is " + queue.rear() + "<br>");
// This code is contributed by Susobhan Akhuli
</script>
Output
10 enqueued to queue 20 enqueued to queue 30 enqueued to queue 40 enqueued to queue 10 dequeued from queue Front item is 20 Rear item is 40
Complexity Analysis of Operations on Queue
Operations | Time Complexity | Space Complexity |
---|---|---|
Enqueue | O(1) | O(1) |
Dequeue | O(1) | O(1) |
Front | O(1) | O(1) |
Back | O(1) | O(1) |
isEmpty | O(1) | O(1) |
isFull | O(1) | O(1) |
Advantages of Queue:
- A large amount of data can be managed efficiently with ease.
- Operations such as insertion and deletion can be performed with ease as it follows the first in first out rule.
- Queues are useful when a particular service is used by multiple consumers.
- Queues are fast in speed for data inter-process communication.
- Queues can be used in the implementation of other data structures.
Disadvantages of Queue:
- The operations such as insertion and deletion of elements from the middle are time consuming.
- Searching an element takes O(N) time.
- Maximum size of a queue must be defined prior in case of array implementation.
Applications of Queue:
Application of queue is common. In a computer system, there may be queues of tasks waiting for the printer, for access to disk storage, or even in a time-sharing system, for use of the CPU. Within a single program, there may be multiple requests to be kept in a queue, or one task may create other tasks, which must be done in turn by keeping them in a queue.
- Queue can be used in job scheduling like Printer Spooling.
- Queue can be used where we have a single resource and multiple consumers.
- In a network, a queue is used in devices such as a router/switch and mail queue.
- Queue can be used in various algorithm techniques like Breadth First Search, Topological Sort, etc.
FAQs (Frequently asked questions) on Queue:
1. What data structure can be used to implement a priority queue?
Priority queues can be implemented using a variety of data structures, including linked lists, arrays, binary search trees, and heaps. Priority queues are best implemented using the heap data structure.
3. In data structures, what is a double-ended queue?
In a double-ended queue, elements can be inserted and removed at both ends.
4. What is better, a stack or a queue?
If you want things to come out in the order you put them in, use a queue. Stacks are useful when you want to reorder things after putting them in.
5. Is Queue a LIFO or FIFO?
Queue follows FIFO (First-In-First-Out) principle, so the element which is inserted first will be deleted first.
6. What are the four types of Queue?
The four types of Queue are: Simple Queue, Double-ended queue, Circular Queue and Priority Queue.
7. What are some real-life applications of Queue?
Real-life applications of queue include: Cashier Line in Stores, CPU Scheduling, Disk Scheduling, Serving requests on a shared resource like Printer.
8. What are some limitations of Queue?
Some of the limitations of Queue are: deletion of some middle element is not possible until all the elements before it are not deleted. Also random access of any element takes linear time complexity.
9. What are the different ways to implement a Queue?
Queue data structure can be implemented by using Arrays or by using Linked List. The Array implementation requires the size of the Queue to be specified at the time of declaration.
10. What are some common operations in Queue?
Some common operations on Queue are: Insertion or Enqueue, Deletion or Dequeue, Front, Rear, isFull, isEmpty, etc.
Related articles: