0% found this document useful (0 votes)
2 views51 pages

Comprehinsive Note On Queue Data Strucuture

The document provides a comprehensive overview of queue data structures, detailing their FIFO principle, basic operations (enqueue, dequeue, peek, isEmpty, isFull), and various types including Simple Queue, Circular Queue, Priority Queue, Double-Ended Queue (Deque), and Concurrent Queue. It also highlights applications of queues in CPU scheduling, print management, network data handling, and graph traversal, along with implementations in Java, Python, and C++. Additionally, it covers key differences in implementations across programming languages and includes code examples for each type of queue.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views51 pages

Comprehinsive Note On Queue Data Strucuture

The document provides a comprehensive overview of queue data structures, detailing their FIFO principle, basic operations (enqueue, dequeue, peek, isEmpty, isFull), and various types including Simple Queue, Circular Queue, Priority Queue, Double-Ended Queue (Deque), and Concurrent Queue. It also highlights applications of queues in CPU scheduling, print management, network data handling, and graph traversal, along with implementations in Java, Python, and C++. Additionally, it covers key differences in implementations across programming languages and includes code examples for each type of queue.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 51

Queue Data Structures: A Comprehensive Note

Introduction to Queue

A queue is a linear data structure that follows the First-In-First-Out (FIFO)


principle, meaning that the first element added to the queue will be the first
one to be removed. It is an abstract data type used in various scenarios,
such as handling tasks, managing resource scheduling, or solving problems
where data must be processed in a specific order.

Queues are widely used in computer science, particularly in operations


involving asynchronous data, multi-threading, task scheduling, and breadth-
first search in graph traversal.

Basic Operations of a Queue

Enqueue: Add an element to the back (rear) of the queue.

Dequeue: Remove an element from the front of the queue.

Peek/Front: View the element at the front of the queue without removing it.

IsEmpty: Check if the queue is empty.

IsFull (optional in fixed-sized queues): Check if the queue has reached its
maximum capacity.

Types of Queues
1. Simple Queue (Linear Queue)

The most basic type, where insertion occurs at the rear, and deletion occurs
at the front.

Once the queue is full, no more elements can be added until some are
removed, leading to wastage of space in fixed-size arrays.

Operations:

Enqueue at the rear.

Dequeue at the front.

Use case: Handling customer requests where the first request is processed
first.

2. Circular Queue

Unlike a simple queue, in a circular queue, the last position is connected to


the first position to make a circle.

This prevents wastage of space as elements can be added to the front once
there is space, forming a loop.
Use case: Resource allocation in circular scheduling, such as CPU scheduling.

3. Priority Queue

Elements are added based on their priority, not just their arrival time.

Highest priority elements are dequeued first.

Use case: Task scheduling systems, such as job queues in an operating


system or handling emergency cases in a hospital.

4. Double-Ended Queue (Deque)

A double-ended queue allows insertion and deletion from both the front and
rear ends.

It can operate as both a stack and a queue.

Use case: Caching systems, undo functionality in software applications.


5. Concurrent Queue

Designed to handle multiple threads simultaneously, without corrupting data


or causing race conditions.

Used in multi-threaded or asynchronous environments.

Use case: Managing thread-safe task queues in multi-core or distributed


systems.

Applications of Queues

1. CPU Task Scheduling: The operating system uses queues to manage


processes that require CPU time.

2. Print Queue: Printers use queues to manage print jobs in the order they
are received.

3. Network Data Packets: Routers and switches use queues to manage the
flow of data packets in a network.

4. Breadth-First Search (BFS): Queues are used in BFS algorithms to traverse


graphs level by level.
5. Asynchronous Data Transfer: Queues manage asynchronous tasks in
systems like message queues (e.g., RabbitMQ or Kafka) for handling real-
time data streams.

6. Simulation and Modeling: Queues are used to simulate and model


processes like customer service systems, inventory management, etc.

Queue Implementations

1. Java Implementation

import java.util.LinkedList;

import java.util.Queue;

public class QueueExample {

public static void main(String[] args) {

Queue<Integer> queue = new LinkedList<>();

// Enqueue elements

queue.add(1);

queue.add(2);

queue.add(3);
System.out.println("Queue: " + queue);

// Dequeue elements

int removedElement = queue.remove();

System.out.println("Removed Element: " + removedElement);

// Peek element

int front = queue.peek();

System.out.println("Front Element: " + front);

// Check if queue is empty

System.out.println("Is Queue Empty? " + queue.isEmpty());

Without built-in classes:

Class Queue {

Private int[] arr;

Private int front;

Private int rear;

Private int capacity;

Private int count;

Public Queue(int size) {

Arr = new int[size];

Capacity = size;

Front = 0;

Rear = -1;
Count = 0;

// Enqueue

Public void enqueue(int item) {

If (count == capacity) {

System.out.println(“Queue is full”);

Return;

Rear = (rear + 1) % capacity;

Arr[rear] = item;

Count++;

// Dequeue

Public void dequeue() {

If (count == 0) {

System.out.println(“Queue is empty”);

Return;

Front = (front + 1) % capacity;

Count--;

// Peek
Public int peek() {

If (count == 0) {

System.out.println(“Queue is empty”);

Return -1;

Return arr[front];

// Check if empty

Public boolean isEmpty() {

Return count == 0;

// Check if full

Public boolean isFull() {

Return count == capacity;

Public class Main {

Public static void main(String[] args) {

Queue queue = new Queue(5);

Queue.enqueue(1);

Queue.enqueue(2);

Queue.enqueue(3);

System.out.println(“Front element: “ + queue.peek());


Queue.dequeue();

System.out.println(“Front element after dequeue: “ + queue.peek());

2. Python Implementation

from collections import deque

# Create a queue

queue = deque()

# Enqueue elements

queue.append(1)

queue.append(2)

queue.append(3)

print("Queue:", queue)

# Dequeue elements

removed_element = queue.popleft()

print("Removed Element:", removed_element)

# Peek element

front_element = queue[0]

print("Front Element:", front_element)


# Check if queue is empty

is_empty = len(queue) == 0

print("Is Queue Empty?", is_empty)

Without built-in classes:

Class Queue:

Def __init__(self, size):

Self.queue = [None] * size

Self.front = 0

Self.rear = -1

Self.capacity = size

Self.count = 0

# Enqueue

Def enqueue(self, item):

If self.count == self.capacity:

Print(“Queue is full”)

Return

Self.rear = (self.rear + 1) % self.capacity

Self.queue[self.rear] = item

Self.count += 1

# Dequeue

Def dequeue(self):

If self.count == 0:

Print(“Queue is empty”)

Return

Self.front = (self.front + 1) % self.capacity


Self.count -= 1

# Peek

Def peek(self):

If self.count == 0:

Print(“Queue is empty”)

Return None

Return self.queue[self.front]

# Check if empty

Def is_empty(self):

Return self.count == 0

# Check if full

Def is_full(self):

Return self.count == self.capacity

# Usage example

Queue = Queue(5)

Queue.enqueue(10)

Queue.enqueue(20)

Queue.enqueue(30)

Print(“Front element:”, queue.peek())

Queue.dequeue()

Print(“Front element after dequeue:”, queue.peek())

3. C++ Implementation
#include <iostream>

#include <queue>

int main() {

std::queue<int> queue;

// Enqueue elements

queue.push(1);

queue.push(2);

queue.push(3);

std::cout << "Queue: ";

std::queue<int> temp = queue; // Copy to print without dequeuing

while (!temp.empty()) {

std::cout << temp.front() << " ";

temp.pop();

std::cout << std::endl;

// Dequeue element

int removedElement = queue.front();

queue.pop();

std::cout << "Removed Element: " << removedElement << std::endl;

// Peek element

int frontElement = queue.front();


std::cout << "Front Element: " << frontElement << std::endl;

// Check if queue is empty

std::cout << "Is Queue Empty? " << (queue.empty() ? "Yes" : "No") <<
std::endl;

return 0;

Without built-in classes:

#include <iostream>

Using namespace std;

Class Queue {

Private:

Int* arr;

Int front;

Int rear;

Int capacity;

Int count;

Public:

Queue(int size) {

Arr = new int[size];

Capacity = size;

Front = 0;

Rear = -1;

Count = 0;
}

// Enqueue

Void enqueue(int item) {

If (count == capacity) {

Cout << “Queue is full” << endl;

Return;

Rear = (rear + 1) % capacity;

Arr[rear] = item;

Count++;

// Dequeue

Void dequeue() {

If (count == 0) {

Cout << “Queue is empty” << endl;

Return;

Front = (front + 1) % capacity;

Count--;

// Peek

Int peek() {

If (count == 0) {

Cout << “Queue is empty” << endl;


Return -1;

Return arr[front];

// Check if empty

Bool isEmpty() {

Return count == 0;

// Check if full

Bool isFull() {

Return count == capacity;

};

Int main() {

Queue queue(5);

Queue.enqueue(10);

Queue.enqueue(20);

Queue.enqueue(30);

Cout << “Front element: “ << queue.peek() << endl;

Queue.dequeue();

Cout << “Front element after dequeue: “ << queue.peek() << endl;
Return 0;

Key Differences in Implementations

Java: Uses Queue interface and LinkedList class for queue operations. Java's
Queue interface offers various methods like add(), remove(), peek(), and
others.

Python: Uses deque from the collections module, which is a highly efficient
double-ended queue implementation.

C++: Utilizes the queue class from the Standard Template Library (STL),
providing a simple interface with push(), pop(), and front() methods.

a complete implementation of a Queue and its various types in Python,


including Simple Queue, Circular Queue, Priority Queue, and Deque (Double-
Ended Queue), along with all the common operations and practical usages.

1. Simple Queue Implementation (Linear Queue)

Class SimpleQueue:

Def __init__(self, size):

Self.queue = [None] * size

Self.front = 0

Self.rear = -1

Self.capacity = size

Self.count = 0

# Enqueue operation

Def enqueue(self, item):


If self.is_full():

Print(“Queue is full”)

Return

Self.rear += 1

Self.queue[self.rear] = item

Self.count += 1

# Dequeue operation

Def dequeue(self):

If self.is_empty():

Print(“Queue is empty”)

Return

Removed = self.queue[self.front]

Self.queue[self.front] = None

Self.front += 1

Self.count -= 1

Return removed

# Peek operation

Def peek(self):

If self.is_empty():

Print(“Queue is empty”)

Return None

Return self.queue[self.front]

# Check if the queue is empty

Def is_empty(self):
Return self.count == 0

# Check if the queue is full

Def is_full(self):

Return self.count == self.capacity

# Display queue contents

Def display(self):

Print(“Queue:”, self.queue)

# Usage example

Queue = SimpleQueue(5)

Queue.enqueue(10)

Queue.enqueue(20)

Queue.enqueue(30)

Queue.display()

Print(“Front element:”, queue.peek())

Queue.dequeue()

Queue.display()

2. Circular Queue Implementation

Class CircularQueue:

Def __init__(self, size):

Self.queue = [None] * size

Self.front = 0
Self.rear = -1

Self.capacity = size

Self.count = 0

# Enqueue operation

Def enqueue(self, item):

If self.is_full():

Print(“Queue is full”)

Return

Self.rear = (self.rear + 1) % self.capacity

Self.queue[self.rear] = item

Self.count += 1

# Dequeue operation

Def dequeue(self):

If self.is_empty():

Print(“Queue is empty”)

Return None

Removed = self.queue[self.front]

Self.queue[self.front] = None

Self.front = (self.front + 1) % self.capacity

Self.count -= 1

Return removed

# Peek operation

Def peek(self):

If self.is_empty():
Print(“Queue is empty”)

Return None

Return self.queue[self.front]

# Check if the queue is empty

Def is_empty(self):

Return self.count == 0

# Check if the queue is full

Def is_full(self):

Return self.count == self.capacity

# Display queue contents

Def display(self):

Print(“Circular Queue:”, self.queue)

# Usage example

Cq = CircularQueue(5)

Cq.enqueue(1)

Cq.enqueue(2)

Cq.enqueue(3)

Cq.display()

Print(“Dequeued element:”, cq.dequeue())

Cq.display()

3. Priority Queue Implementation


In a priority queue, each element is associated with a priority, and elements
are dequeued based on their priority (higher priority first).

Class PriorityQueue:

Def __init__(self):

Self.queue = []

# Enqueue operation

Def enqueue(self, item, priority):

Self.queue.append((item, priority))

Self.queue.sort(key=lambda x: x[1]) # Sort by priority (lowest to


highest)

# Dequeue operation

Def dequeue(self):

If self.is_empty():

Print(“Priority Queue is empty”)

Return None

Return self.queue.pop(0) # Remove the element with the highest


priority

# Peek operation

Def peek(self):

If self.is_empty():

Print(“Priority Queue is empty”)

Return None

Return self.queue[0]
# Check if the queue is empty

Def is_empty(self):

Return len(self.queue) == 0

# Display queue contents

Def display(self):

Print(“Priority Queue:”, self.queue)

# Usage example

Pq = PriorityQueue()

Pq.enqueue(“Low Priority Task”, 3)

Pq.enqueue(“High Priority Task”, 1)

Pq.enqueue(“Medium Priority Task”, 2)

Pq.display()

Print(“Dequeued:”, pq.dequeue())

Pq.display()

4. Deque (Double-Ended Queue) Implementation

A deque allows inserting and removing elements from both the front and
rear.

Class Deque:

Def __init__(self, size):

Self.queue = [None] * size


Self.front = 0

Self.rear = -1

Self.capacity = size

Self.count = 0

# Insert at the front

Def insert_front(self, item):

If self.is_full():

Print(“Deque is full”)

Return

Self.front = (self.front – 1) % self.capacity

Self.queue[self.front] = item

Self.count += 1

# Insert at the rear

Def insert_rear(self, item):

If self.is_full():

Print(“Deque is full”)

Return

Self.rear = (self.rear + 1) % self.capacity

Self.queue[self.rear] = item

Self.count += 1

# Delete from the front

Def delete_front(self):

If self.is_empty():

Print(“Deque is empty”)
Return None

Removed = self.queue[self.front]

Self.queue[self.front] = None

Self.front = (self.front + 1) % self.capacity

Self.count -= 1

Return removed

# Delete from the rear

Def delete_rear(self):

If self.is_empty():

Print(“Deque is empty”)

Return None

Removed = self.queue[self.rear]

Self.queue[self.rear] = None

Self.rear = (self.rear – 1) % self.capacity

Self.count -= 1

Return removed

# Peek at the front

Def peek_front(self):

If self.is_empty():

Print(“Deque is empty”)

Return None

Return self.queue[self.front]

# Peek at the rear

Def peek_rear(self):
If self.is_empty():

Print(“Deque is empty”)

Return None

Return self.queue[self.rear]

# Check if the deque is empty

Def is_empty(self):

Return self.count == 0

# Check if the deque is full

Def is_full(self):

Return self.count == self.capacity

# Display deque contents

Def display(self):

Print(“Deque:”, self.queue)

# Usage example

Dq = Deque(5)

Dq.insert_rear(10)

Dq.insert_front(20)

Dq.insert_rear(30)

Dq.display()

Print(“Front element:”, dq.peek_front())

Print(“Rear element:”, dq.peek_rear())

Dq.delete_front()
Dq.delete_rear()

Dq.display()

Practical Usages of Queues:

1. Simple Queue: Useful for task scheduling where tasks must be


executed in the order they arrive, such as in a printer queue or
customer service requests.

2. Circular Queue: Circular queues are ideal for scenarios like circular CPU
scheduling or managing buffers where old data needs to be
overwritten.

3. Priority Queue: Used in task management systems where certain tasks


have higher priority than others, like scheduling tasks in an operating
system or managing hospital patients based on urgency.

4. Deque: A deque is useful when you need the flexibility of both stack
(LIFO) and queue (FIFO) behavior, such as in caching systems or
undo/redo operations in software.

Concurrent Queues in Python

Concurrent queues are essential in multi-threaded or asynchronous


environments where multiple threads or processes need to interact with the
same queue without causing race conditions or corrupting data. These
queues ensure thread safety, meaning that two threads can concurrently
enqueue and dequeue without interference.
Python provides thread-safe queues with the queue module, which is part of
the standard library. However, since you’re looking for a custom
implementation, I’ll demonstrate how to implement a basic concurrent queue
using threading and locks for manual concurrency control.

Key Concepts:

Concurrency: Multiple tasks can run simultaneously, but they must ensure
data integrity when sharing resources.

Locks: Used to ensure that only one thread can access a shared resource
(queue) at a time.

Implementation of a Simple Thread-Safe (Concurrent) Queue in Python

We’ll use the threading module and the Lock object to ensure that only one
thread can modify the queue at any time.

1. Thread-Safe Queue Implementation

Import threading

Class ConcurrentQueue:

Def __init__(self, size):

Self.queue = [None] * size

Self.front = 0

Self.rear = -1
Self.capacity = size

Self.count = 0

Self.lock = threading.Lock()

# Enqueue operation with thread safety

Def enqueue(self, item):

With self.lock:

If self.is_full():

Print(“Queue is full”)

Return

Self.rear = (self.rear + 1) % self.capacity

Self.queue[self.rear] = item

Self.count += 1

Print(f”Enqueued: {item}”)

# Dequeue operation with thread safety

Def dequeue(self):

With self.lock:

If self.is_empty():

Print(“Queue is empty”)

Return None

Removed = self.queue[self.front]

Self.queue[self.front] = None

Self.front = (self.front + 1) % self.capacity

Self.count -= 1

Print(f”Dequeued: {removed}”)

Return removed
# Peek operation with thread safety

Def peek(self):

With self.lock:

If self.is_empty():

Print(“Queue is empty”)

Return None

Return self.queue[self.front]

# Check if the queue is empty

Def is_empty(self):

Return self.count == 0

# Check if the queue is full

Def is_full(self):

Return self.count == self.capacity

# Display the queue (for demonstration)

Def display(self):

With self.lock:

Print(“Queue:”, self.queue)

# Testing concurrent queue with multiple threads

Import time

Import threading
Def producer(queue, items):

For item in items:

Queue.enqueue(item)

Time.sleep(0.1)

Def consumer(queue):

While True:

Queue.dequeue()

Time.sleep(0.2)

# Usage example

Queue = ConcurrentQueue(5)

# Create producer and consumer threads

Producer_thread = threading.Thread(target=producer, args=(queue, [1, 2, 3,


4, 5]))

Consumer_thread = threading.Thread(target=consumer, args=(queue,))

# Start the threads

Producer_thread.start()

Consumer_thread.start()

# Join the threads (wait for them to finish)

Producer_thread.join()

# Allow the consumer to finish processing

Time.sleep(1)
Explanation:

Locking Mechanism: The threading.Lock() is used to ensure mutual exclusion.


Only one thread can enqueue or dequeue at a time.

Enqueue and Dequeue Operations: These operations are wrapped with with
self.lock: to ensure that the queue operations are atomic (they cannot be
interrupted by another thread).

Producer-Consumer Problem: This is a classic problem where producers


(threads that enqueue items) and consumers (threads that dequeue items)
run concurrently. The producer adds items to the queue, and the consumer
removes them.

2. Practical Usage of Concurrent Queue

Concurrent queues are especially useful in scenarios involving multiple


threads or processes:

Producer-Consumer Problem: Where one set of threads produces data and


another consumes it. For example:

Web Server: Handling multiple HTTP requests where threads produce


requests, and a thread pool processes those requests.

Message Queuing: Message systems like RabbitMQ and Kafka use concurrent
queues to manage asynchronous messaging between different systems or
processes.
Task Scheduling: Multi-threaded programs that need to schedule tasks can
use concurrent queues to store tasks that are processed by worker threads.

Real-Time Data Processing: Systems that handle real-time data (e.g., sensor
data, logs) often use concurrent queues to buffer data between producers
(e.g., sensors, data sources) and consumers (e.g., data processors,
databases).

Benefits of Concurrent Queues:

Thread Safety: Prevents data corruption when multiple threads try to access
the queue at the same time.

Efficient Resource Sharing: Multiple threads can use a shared queue to


communicate or share data without conflicts.

Coordination: Useful for coordinating work between producer and consumer


threads in a safe and organized manner.

Notes on Further Improvements:

Condition Variables: You can improve this basic concurrent queue by using
condition variables (e.g., threading.Condition()) to signal waiting threads
when the queue is full or empty. This is particularly useful to avoid busy-
waiting in the consumer when the queue is empty.
Queue Overflow: This implementation handles queue overflow by ignoring
enqueue attempts when the queue is full. In some applications, it’s better to
block the producer thread until space is available (again, using condition
variables).

This custom implementation of a concurrent queue shows how to manually


handle synchronization issues using Python’s threading and lock
mechanisms. However, for real-world projects, it’s often better to use
Python’s queue.Queue, which provides a robust, thread-safe queue
implementation out of the box.

These implementations demonstrate how to manually create different types


of queues and manage their operations effectively without relying on built-in
Python classes like deque.

Here’s the complete implementation of various queue types in Java,


including Simple Queue, Circular Queue, Priority Queue, and Deque (Double-
Ended Queue). These implementations do not rely on built-in Java collections
like LinkedList or PriorityQueue and demonstrate manual queue creation with
all the necessary operations.

1. Simple Queue (Linear Queue) Implementation

Class SimpleQueue {

Private int[] queue;

Private int front;

Private int rear;

Private int capacity;

Private int count;


Public SimpleQueue(int size) {

Queue = new int[size];

Capacity = size;

Front = 0;

Rear = -1;

Count = 0;

// Enqueue operation

Public void enqueue(int item) {

If (isFull()) {

System.out.println(“Queue is full”);

Return;

Rear++;

Queue[rear] = item;

Count++;

System.out.println(“Enqueued: “ + item);

// Dequeue operation

Public int dequeue() {

If (isEmpty()) {

System.out.println(“Queue is empty”);

Return -1;

Int removed = queue[front];


For (int I = 0; I < rear; i++) {

Queue[i] = queue[I + 1];

Rear--;

Count--;

Return removed;

// Peek operation

Public int peek() {

If (isEmpty()) {

System.out.println(“Queue is empty”);

Return -1;

Return queue[front];

// Check if the queue is empty

Public boolean isEmpty() {

Return count == 0;

// Check if the queue is full

Public boolean isFull() {

Return count == capacity;

}
// Display queue contents

Public void display() {

System.out.print(“Queue: “);

For (int I = 0; I <= rear; i++) {

System.out.print(queue[i] + “ “);

System.out.println();

Public static void main(String[] args) {

SimpleQueue queue = new SimpleQueue(5);

Queue.enqueue(10);

Queue.enqueue(20);

Queue.enqueue(30);

Queue.display();

System.out.println(“Dequeued: “ + queue.dequeue());

Queue.display();

2. Circular Queue Implementation

Class CircularQueue {

Private int[] queue;

Private int front;

Private int rear;

Private int capacity;


Private int count;

Public CircularQueue(int size) {

Queue = new int[size];

Capacity = size;

Front = 0;

Rear = -1;

Count = 0;

// Enqueue operation

Public void enqueue(int item) {

If (isFull()) {

System.out.println(“Queue is full”);

Return;

Rear = (rear + 1) % capacity;

Queue[rear] = item;

Count++;

System.out.println(“Enqueued: “ + item);

// Dequeue operation

Public int dequeue() {

If (isEmpty()) {

System.out.println(“Queue is empty”);

Return -1;
}

Int removed = queue[front];

Front = (front + 1) % capacity;

Count--;

Return removed;

// Peek operation

Public int peek() {

If (isEmpty()) {

System.out.println(“Queue is empty”);

Return -1;

Return queue[front];

// Check if the queue is empty

Public boolean isEmpty() {

Return count == 0;

// Check if the queue is full

Public boolean isFull() {

Return count == capacity;

// Display queue contents


Public void display() {

System.out.print(“Circular Queue: “);

For (int I = 0; I < count; i++) {

System.out.print(queue[(front + i) % capacity] + “ “);

System.out.println();

Public static void main(String[] args) {

CircularQueue queue = new CircularQueue(5);

Queue.enqueue(10);

Queue.enqueue(20);

Queue.enqueue(30);

Queue.display();

System.out.println(“Dequeued: “ + queue.dequeue());

Queue.display();

3. Priority Queue Implementation

In this implementation, the priority is represented as an integer, and the


lower the number, the higher the priority.

Class PriorityQueue {

Private int[][] queue;

Private int size;


Private int capacity;

Public PriorityQueue(int capacity) {

This.capacity = capacity;

Queue = new int[capacity][2]; // [0]: value, [1]: priority

Size = 0;

// Enqueue with priority

Public void enqueue(int item, int priority) {

If (isFull()) {

System.out.println(“Priority Queue is full”);

Return;

Queue[size][0] = item;

Queue[size][1] = priority;

Size++;

sortByPriority();

// Sort the queue by priority (ascending order)

Private void sortByPriority() {

For (int I = 0; I < size – 1; i++) {

For (int j = 0; j < size – I – 1; j++) {

If (queue[j][1] > queue[j + 1][1]) {

// Swap

Int[] temp = queue[j];


Queue[j] = queue[j + 1];

Queue[j + 1] = temp;

// Dequeue (removes the highest priority element)

Public int dequeue() {

If (isEmpty()) {

System.out.println(“Priority Queue is empty”);

Return -1;

Int removed = queue[0][0];

For (int I = 0; I < size – 1; i++) {

Queue[i] = queue[I + 1];

Size--;

Return removed;

// Peek at the highest priority element

Public int peek() {

If (isEmpty()) {

System.out.println(“Priority Queue is empty”);

Return -1;

}
Return queue[0][0];

// Check if the priority queue is empty

Public boolean isEmpty() {

Return size == 0;

// Check if the priority queue is full

Public boolean isFull() {

Return size == capacity;

// Display the priority queue

Public void display() {

System.out.print(“Priority Queue: “);

For (int I = 0; I < size; i++) {

System.out.print(“(“ + queue[i][0] + “, Priority: “ + queue[i][1] + “)


“);

System.out.println();

Public static void main(String[] args) {

PriorityQueue pq = new PriorityQueue(5);

Pq.enqueue(10, 2);

Pq.enqueue(20, 1);
Pq.enqueue(30, 3);

Pq.display();

System.out.println(“Dequeued: “ + pq.dequeue());

Pq.display();

4. Deque (Double-Ended Queue) Implementation

Class Deque {

Private int[] deque;

Private int front;

Private int rear;

Private int capacity;

Private int count;

Public Deque(int size) {

Deque = new int[size];

Capacity = size;

Front = 0;

Rear = -1;

Count = 0;

// Insert at the front

Public void insertFront(int item) {

If (isFull()) {
System.out.println(“Deque is full”);

Return;

Front = (front – 1 + capacity) % capacity;

Deque[front] = item;

Count++;

System.out.println(“Inserted at front: “ + item);

// Insert at the rear

Public void insertRear(int item) {

If (isFull()) {

System.out.println(“Deque is full”);

Return;

Rear = (rear + 1) % capacity;

Deque[rear] = item;

Count++;

System.out.println(“Inserted at rear: “ + item);

// Delete from the front

Public int deleteFront() {

If (isEmpty()) {

System.out.println(“Deque is empty”);

Return -1;

}
Int removed = deque[front];

Front = (front + 1) % capacity;

Count--;

Return removed;

// Delete from the rear

Public int deleteRear() {

If (isEmpty()) {

System.out.println(“Deque is empty”);

Return -1;

Int removed = deque[rear];

Rear = (rear – 1 + capacity) % capacity;

Count--;

Return removed;

// Peek at the front

Public int peekFront() {

If (isEmpty()) {

System.out.println(“Deque is empty”);

Return -1;

Return deque[front];

}
// Peek at the rear

Public int peekRear() {

If (isEmpty()) {

System.out.println(“Deque is empty”);

Return -1;

Return deque[rear];

// Check if the deque is empty

Public boolean isEmpty() {

Return count == 0;

// Check if the deque is full

Public boolean isFull() {

Return count == capacity;

// Display deque contents

Public void display() {

System.out.print(“Deque: “);

For (int I = 0; I < count; i++) {

System.out.print(deque[(front + i) % capacity] + “ “);

System.out.println();
}

Public static void main(String[] args) {

Deque deque = new Deque(5);

// Inserting at the rear

Deque.insertRear(10);

Deque.insertRear(20);

Deque.insertRear(30);

Deque.display();

// Inserting at the front

Deque.insertFront(40);

Deque.insertFront(50);

Deque.display();

// Deleting from the rear

System.out.println(“Deleted from rear: “ + deque.deleteRear());

Deque.display();

// Deleting from the front

System.out.println(“Deleted from front: “ + deque.deleteFront());

Deque.display();

// Peeking front and rear

System.out.println(“Front element: “ + deque.peekFront());

System.out.println(“Rear element: “ + deque.peekRear());


}

Explanation of Operations in Deque:

1. Insert at Front (insertFront):

Inserts an element at the front of the deque. This is done by adjusting the
front pointer circularly to move backward, allowing the element to be
inserted at the front.

2. Insert at Rear (insertRear):

Inserts an element at the rear (back) of the deque. This is done by moving
the rear pointer forward circularly.

3. Delete from Front (deleteFront):

Removes an element from the front of the deque by adjusting the front
pointer to the next element in the queue. The removed element is returned.

4. Delete from Rear (deleteRear):


Removes an element from the rear of the deque by adjusting the rear pointer
backward in a circular manner. The removed element is returned.

5. Peek Front and Rear (peekFront, peekRear):

These operations return the elements at the front and rear, respectively,
without modifying the queue.

6. Display:

The display() method shows the current elements in the deque.

Key Points:

Circular Buffer: The Deque uses a circular buffer to ensure efficient memory
use and avoids the need for shifting elements as would be required in a
simple queue.

Capacity Handling: The capacity is checked before each insert to prevent


overflow, and before each delete to ensure there are elements to remove.

Efficiency: Since all operations are performed using index manipulation


without shifting elements, operations are O(1) in most cases.
Summary of Queue Types:

1. Simple Queue: Follows FIFO, with basic enqueue and dequeue


operations.

2. Circular Queue: Avoids wasted space in fixed-size queues by wrapping


around to the beginning.

3. Priority Queue: Elements are dequeued based on priority rather than


insertion order.

4. Deque (Double-Ended Queue): Supports insertion and deletion from


both ends, making it flexible for scenarios requiring both stack and
queue behaviors.

These implementations manually manage arrays and indices to achieve the


desired queue behaviors without relying on Java’s built-in collection classes.

Summary of Key Points:

Queues are important linear data structures following FIFO.


Types include Simple Queue, Circular Queue, Priority Queue, Deque, and
Concurrent Queue.

Applications include task scheduling, BFS traversal, and resource allocation


in operating systems.

Implementing queues in Java, Python, and C++ involves similar operations


but different language-specific interfaces.

You might also like