1. Home
Data Structure

Data Structure Tutorial: Everything You Need to Know

Learn all about data structures with our comprehensive tutorial. Master the fundamentals and advance your skills in organizing and managing data efficiently.

  • 60
  • 14
right-top-arrow

Tutorial Playlist

58 Lessons
51

Queue in Data Structure: Types, Implementation, and Applications

Updated on 23/08/2024435 Views

Introduction

When it comes to data structures, queue is one of the fundamental and widely used concepts. A queue is a linear data structure that follows the First In, First Out (FIFO) principle, meaning the element that is inserted first will be the first one to be removed. This is similar to a real-life queue or line of people waiting for a service, where the person joining the line gets served first.

Queue is an essential data structure in my programming journey. It has been crucial in solving various problems and implementing algorithms efficiently. In this article, I will dive deep into the concept of queue, its types, implementation, and real-world applications. By the end of this article, you will have a solid understanding of queue and how to utilize it effectively in your projects.

Definition and Fundamental Concepts

A queue is an abstract data type representing a collection of elements with two primary operations: enqueue and dequeue. The enqueue operation adds an element to the rear or back of the queue, while the dequeue operation removes an element from the front of the queue. This behavior follows the FIFO principle, ensuring that the first element added is the first one to be removed.

Here are the fundamental concepts related to queues:

  1. FIFO (First In, First Out): As mentioned earlier, the queue follows the FIFO principle. The element inserted first will be the first one to be removed.
  2. Enqueue: The operation of adding an element to the rear or back of the queue is called enqueue. It increases the size of the queue by one.
  3. Dequeue: The operation of removing an element from the front of the queue is called dequeue. It decreases the size of the queue by one.
  4. Front: The front of the queue refers to the first element inserted and will be the next one to be removed.
  5. Rear: The rear or back of the queue refers to the last element that was inserted.
  6. Overflow: When we try to insert an element into an already full queue, it is called an overflow condition.
  7. Underflow: When we try to remove an element from an empty queue, it is called an underflow condition.

Understanding these fundamental concepts is crucial for working with queues effectively.

Types of Queue in Data Structure

There are several types of queues, each with its characteristics and use cases. Let us explore the different types of queues in detail:

Simple Queue

A simple queue, also known as a linear queue, is the most basic type of queue. It follows the FIFO principle strictly. Elements are inserted at the rear end and removed from the front end. The front and rear pointers move linearly as elements are added or removed.

Class Queue:

    def __init__(self):

        self.items = []

    def is_empty(self):

        return len(self.items) == 0

    def enqueue(self, item):

        self. items.append(item)

    def dequeue(self):

        if not self.is_empty():

            return self. items.pop(0)

    def front(self):

        if not self.is_empty():

            return self.items[0]

    def size(self):

        return len(self.items)

Circular Queue

A circular queue is a variation of the simple queue where the last element is connected back to the first element, forming a circular structure. This type of queue optimizes memory utilization. When the rear pointer reaches the end of the queue, it wraps around to the beginning if there is space available at the front.

class CircularQueue:

    def __init__(self, max_size):

        self.items = [None] * max_size

        self.max_size = max_size

        self.front = -1

        self.rear = -1

    def is_empty(self):

        return self.front == -1

    def is_full(self):

        return (self.rear + 1) % self.max_size == self.front

    def enqueue(self, item):

        if self.is_full():

            print("Queue is full.")

            return

        if self.is_empty():

            self.front = 0

        self.rear = (self.rear + 1) % self.max_size

        self.items[self.rear] = item

    def dequeue(self):

        if self.is_empty():

            print("Queue is empty.")

            return

        item = self.items[self.front]

        if self.front == self.rear:

            self.front = -1

            self.rear = -1

        else:

            self.front = (self.front + 1) % self.max_size

        return item

Priority Queue

A priority queue is a specialized queue where each element is associated with a priority value. Elements with higher priority are dequeued before elements with lower priority, irrespective of their insertion order. Priority queues are commonly implemented using a heap data structure.

import heapq

class PriorityQueue:

    def __init__(self):

        self.items = []

    def is_empty(self):

        return len(self.items) == 0

    def enqueue(self, item, priority):

        heapq.heappush(self.items, (priority, item))

    def dequeue(self):

        if not self.is_empty():

            return heapq.heappop(self.items)[1]

    def front(self):

        if not self.is_empty():

            return self.items[0][1]

    def size(self):

        return len(self.items)

Double-ended Queue (Deque)

A double-ended queue, or deque, is a queue-like data structure that allows the insertion and deletion of elements from both ends. It combines the features of a stack and a queue. Elements can be added or removed from the deque's front or rear.

class Deque:

    def __init__(self):

        self.items = []

    def is_empty(self):

        return len(self.items) == 0

    def add_front(self, item):

        self.items.insert(0, item)

    def add_rear(self, item):

        self.items.append(item)

    def remove_front(self):

        if not self.is_empty():

            return self.items.pop(0)

    def remove_rear(self):

        if not self.is_empty():

            return self.items.pop()

    def front(self):

        if not self.is_empty():

            return self.items[0]

    def rear(self):

        if not self.is_empty():

            return self.items[-1]

    def size(self):

        return len(self.items)

These are some of the commonly used types of queues in data structures. Each type has its own characteristics and use cases; understanding them is crucial for solving specific problems efficiently.

Why and Where We Use Queues

Queues are used in various scenarios where we need to maintain the order of elements and process them in a First In First Out (FIFO) manner. Here are some common use cases for queues:

  1. Task Scheduling: Queues are often used in operating systems and task schedulers to manage the execution of processes or tasks. Tasks are enqueued based on their priority or arrival time and dequeued for execution fairly and orderly.
  2. Breadth-First Search (BFS): In graph traversal algorithms like BFS, queues are used to keep track of the nodes to be visited. The nodes are enqueued in the order they are discovered and dequeued for processing, ensuring that all neighbors of a node are visited before moving to the next level.
  3. Buffer Management: Queues are used as buffers to store data between two processes or systems temporarily. For example, in a producer-consumer model, the producer enqueues data into a queue, and the consumer dequeues and processes the data from the queue.
  4. Asynchronous Communication: In asynchronous communication systems, such as message queues or event-driven architectures, queues are used to store and manage incoming messages or events. The messages are enqueued by the sender and dequeued by the receiver for processing.
  5. Printer Spooling: Print jobs are often managed using a queue in a multi-user environment. Print jobs are enqueued as they arrive and dequeued for printing based on the order of arrival or priority.

These are just a few examples of where queues are used. Queues provide a straightforward and efficient way to handle scenarios where the order of elements is important and must be processed in a FIFO manner.

Comparison with Similar Data Structures

Queues share similarities with other data structures, such as stacks and linked lists. Here's a comparison of queues with these similar data structures:

Queue vs. Stack:

  • Queues follow the FIFO (First In First Out) principle, where the first element inserted is the first one to be removed.
  • Stacks follow the LIFO (Last In First Out) principle, where the last element inserted is the first one to be removed.
  • Queues are used when the order of elements needs to be preserved and the first element needs to be processed first.
  • Stacks are used when the order of elements needs to be reversed and the last element needs to be processed first.

Queue vs. Linked List:

  • Queues can be implemented using a linked list data structure, where each element is represented as a node.
  • Linked lists provide dynamic memory allocation and allow for easy insertion and deletion of elements.
  • However, linked lists do not inherently enforce the FIFO order. Additional pointers (front and rear) are needed to maintain the queue order.
  • Queues have specific operations (enqueue and dequeue) that maintain the FIFO order, while linked lists have more general operations (insertion and deletion at any position).

Queue vs. Deque (Double-Ended Queue):

  • Deques are a generalization of queues that allow insertion and deletion from both ends.
  • Queues typically have a front and a rear, and elements can be inserted at the rear and removed from the front.
  • Deques have a front and a rear, and elements can be inserted and removed from both ends.
  • Queues are used when the FIFO order needs to be strictly maintained, while deques offer more flexibility in terms of insertion and deletion operations.

Understanding the differences and similarities between queues and other data structures helps in choosing the appropriate data structure for a given problem or scenario.

Implementation of Queue

Queues can be implemented using various data structures, such as arrays and linked lists. Let's explore the implementation of a simple queue using an array and a linked list.

Array Implementation

In an array implementation, we use a fixed-size array to represent the queue. The front and rear pointers keep track of the first and last elements, respectively. Here's an example implementation in Python:

class ArrayQueue:

    def __init__(self, capacity):

        self.items = [None] * capacity

        self.capacity = capacity

        self.front = -1

        self.rear = -1

def is_empty(self):

        return self.front == -1

def is_full(self):

        return self.rear == self.capacity - 1

def enqueue(self, item):

        if self.is_full():

            print("Queue is full.")

            return

        if self.is_empty():

            self.front = 0

        self.rear += 1

        self.items[self.rear] = item

        print(f"Enqueued: {item}")

def dequeue(self):

        if self.is_empty():

            print("Queue is empty.")

            return

        item = self.items[self.front]

        if self.front == self.rear:

            self.front = -1

            self.rear = -1

        else:

            self.front += 1

        print(f"Dequeued: {item}")

        return item

# Example usage

queue = ArrayQueue(3)

queue.enqueue(10)

queue.enqueue(20)

queue.enqueue(30)

queue.enqueue(40)  # Queue is full

queue.dequeue()

queue.dequeue()

queue.dequeue()

queue.dequeue()  # Queue is empty

Linked List Implementation

In a linked list implementation, each element in the queue is represented as a node. The front pointer points to the first node, and the rear pointer points to the last node. Here's an example implementation in Python:

class Node:

    def __init__(self, data):

        self.data = data

        self.next = None

class LinkedQueue:

    def __init__(self):

        self.front = None

        self.rear = None

def is_empty(self):

        return self.front is None

def enqueue(self, item):

        new_node = Node(item)

        if self.is_empty():

            self.front = new_node

            self.rear = new_node

        else:

            self.rear.next = new_node

            self.rear = new_node

        print(f"Enqueued: {item}")

def dequeue(self):

        if self.is_empty():

            print("Queue is empty.")

            return

        item = self.front.data

        self.front = self.front.next

        if self.front is None:

            self.rear = None

        print(f"Dequeued: {item}")

        return item

# Example usage

queue = LinkedQueue()

queue.enqueue(10)

queue.enqueue(20)

queue.enqueue(30)

queue.dequeue()

queue.dequeue()

queue.dequeue()

queue.dequeue()  # Queue is empty

Both array and linked list implementations have their advantages and trade-offs. Array implementation provides constant-time access to elements but has a fixed size, while linked list implementation allows for dynamic resizing but requires extra memory for pointers.

Advantages and Disadvantages of Queue

Advantages

  1. FIFO Ordering: Queue ensures that elements are processed in the order of their arrival, following the First In First Out (FIFO) principle. This is useful in scenarios where fairness and order are important.
  2. Efficient Insertion and Deletion: Queue provides efficient insertion and deletion operations. Elements can be added to the rear and removed from the front in constant time complexity, making queues suitable for handling large amounts of data.
  3. Versatility: Queues can be used to solve a wide range of problems and are applicable in various domains, such as task scheduling, event handling, breadth-first search, and more.
  4. Easy Implementation: Queues can be easily implemented using arrays or linked lists, making them straightforward to understand and use.

Disadvantages

  1. Fixed Size (Array Implementation): When implementing a queue using an array, the size of the queue is fixed and cannot be dynamically resized. This can lead to overflow or underflow conditions if not properly managed.
  2. Inefficient Random Access: Queues do not provide efficient random access to elements. Accessing an element in the middle of the queue requires traversing from the front, which can be time-consuming.
  3. Blocking Nature: In scenarios where multiple threads or processes are accessing a shared queue, the dequeue operation can block the execution if the queue is empty, leading to potential performance bottlenecks.
  4. Memory Overhead (Linked List Implementation): When implementing a queue using a linked list, each element requires additional memory for the next pointer, resulting in higher memory overhead compared to an array implementation.

It's important to consider these advantages and disadvantages when deciding whether to use a queue for a specific problem or system design.

Conclusion

In this article, we have explored the fundamental concepts of queues in data structures. We have discussed the different types of queues, including simple queues, circular queues, priority queues, and double-ended queues (deque). We have also examined the implementation of queues using arrays and linked lists and examined the advantages and disadvantages of each approach.

Furthermore, we delved into various real-world applications of queues, showcasing their versatility and importance in solving a wide range of problems. From breadth-first search and task scheduling to cache management and network packet buffering, queues play a crucial role in many algorithms and systems.

Understanding the queue concept is essential for any programmer or computer science enthusiast. By leveraging the power of queues, you can design efficient and scalable solutions to complex problems.

Frequently Asked Questions

1. What is stack and queue?

Stack and queue are both linear data structures but differ in operating principles. A stack follows the Last In, First Out (LIFO) principle, where the last element inserted is the first one to be removed. On the other hand, a queue follows the First In First Out (FIFO) principle, where the first element inserted is the first one to be removed.

2. Is the queue a LIFO or FIFO?

Queue is a FIFO (First In, First Out) data structure. The element inserted first will be the first to be removed from the queue.

3. Why is a queue called FIFO?

Queue is called FIFO because it follows the First In, First Out principle. The element that enters the queue first will be the first one to be processed and removed from the queue. This behavior resembles a real-life queue or line, where the person joining the line gets served first.

4. What are the three (3) types of queuing systems?

The three common types of queuing systems are:

  1. Single Server Queue: In this system, only one server serves the customers or processes the requests.
  2. Multi-Server Queue: This system has multiple servers working in parallel to serve the customers or process the requests.
  3. Priority Queue: In a priority queue, elements are assigned priorities, and the element with the highest priority is processed first, regardless of its position in the queue.

5. What are the different types of queues?

The different types of queues include:

  1. Simple Queue: A basic queue that strictly follows the FIFO principle.
  2. Circular Queue: A variation of the simple queue where the last element is connected back to the first element, forming a circular structure.
  3. Priority Queue: A queue where each element is associated with a priority, and elements with higher priorities are processed first.
  4. Double-ended Queue (Deque): A queue-like data structure that allows insertion and deletion of elements from both ends.

6. Which type of queue is best?

The best type of queue depends on the specific requirements and constraints of the problem at hand. Each type of queue has its advantages and use cases. For example, a simple queue is suitable for basic FIFO operations, while a priority queue is ideal when elements need to be processed based on their priorities. The choice of queue type should be based on factors such as the desired ordering, performance requirements, and the nature of the problem being solved.

7. What are the four applications of queues?

Four common applications of queues are:

  1. Breadth-First Search (BFS): Queue is used to keep track of the vertices to be visited in a graph traversal algorithm.
  2. Task Scheduling: Queues are used to manage the execution of processes or tasks based on their priority or arrival time.
  3. Printer Spooling: Print jobs are managed using a queue in a multi-user environment.
  4. Cache Management: Queues are used to implement cache replacement policies like Least Recently Used (LRU) or Least Frequently Used (LFU).

8. What are the applications of the queue?

Queues have numerous applications in various domains. Some notable applications include:

  • Breadth-First Search (BFS) in graph algorithms
  • Task scheduling in operating systems
  • Printer spooling in multi-user environments
  • Cache management in computer systems
  • Event handling in event-driven systems
  • Network packet buffering in network communication
  • Implementing priority queuing systems
Abhimita Debnath

Abhimita Debnath

Abhimita Debnath is one of the students in UpGrad Big Data Engineering program with BITS Pilani. She's a Senior Software Engineer in Infosys. She…Read More

Get Free Career Counselling
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...