Comprehinsive Note On Queue Data Strucuture
Comprehinsive Note On Queue Data Strucuture
Introduction to Queue
Peek/Front: View the element at the front of the queue without removing it.
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:
Use case: Handling customer requests where the first request is processed
first.
2. Circular Queue
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.
A double-ended queue allows insertion and deletion from both the front and
rear ends.
Applications of Queues
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.
Queue Implementations
1. Java Implementation
import java.util.LinkedList;
import java.util.Queue;
// Enqueue elements
queue.add(1);
queue.add(2);
queue.add(3);
System.out.println("Queue: " + queue);
// Dequeue elements
// Peek element
Class Queue {
Capacity = size;
Front = 0;
Rear = -1;
Count = 0;
// Enqueue
If (count == capacity) {
System.out.println(“Queue is full”);
Return;
Arr[rear] = item;
Count++;
// Dequeue
If (count == 0) {
System.out.println(“Queue is empty”);
Return;
Count--;
// Peek
Public int peek() {
If (count == 0) {
System.out.println(“Queue is empty”);
Return -1;
Return arr[front];
// Check if empty
Return count == 0;
// Check if full
Queue.enqueue(1);
Queue.enqueue(2);
Queue.enqueue(3);
2. Python Implementation
# 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()
# Peek element
front_element = queue[0]
is_empty = len(queue) == 0
Class Queue:
Self.front = 0
Self.rear = -1
Self.capacity = size
Self.count = 0
# Enqueue
If self.count == self.capacity:
Print(“Queue is full”)
Return
Self.queue[self.rear] = item
Self.count += 1
# Dequeue
Def dequeue(self):
If self.count == 0:
Print(“Queue is empty”)
Return
# 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):
# Usage example
Queue = Queue(5)
Queue.enqueue(10)
Queue.enqueue(20)
Queue.enqueue(30)
Queue.dequeue()
3. C++ Implementation
#include <iostream>
#include <queue>
int main() {
std::queue<int> queue;
// Enqueue elements
queue.push(1);
queue.push(2);
queue.push(3);
while (!temp.empty()) {
temp.pop();
// Dequeue element
queue.pop();
// Peek element
std::cout << "Is Queue Empty? " << (queue.empty() ? "Yes" : "No") <<
std::endl;
return 0;
#include <iostream>
Class Queue {
Private:
Int* arr;
Int front;
Int rear;
Int capacity;
Int count;
Public:
Queue(int size) {
Capacity = size;
Front = 0;
Rear = -1;
Count = 0;
}
// Enqueue
If (count == capacity) {
Return;
Arr[rear] = item;
Count++;
// Dequeue
Void dequeue() {
If (count == 0) {
Return;
Count--;
// Peek
Int peek() {
If (count == 0) {
Return arr[front];
// Check if empty
Bool isEmpty() {
Return count == 0;
// Check if full
Bool isFull() {
};
Int main() {
Queue queue(5);
Queue.enqueue(10);
Queue.enqueue(20);
Queue.enqueue(30);
Queue.dequeue();
Cout << “Front element after dequeue: “ << queue.peek() << endl;
Return 0;
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.
Class SimpleQueue:
Self.front = 0
Self.rear = -1
Self.capacity = size
Self.count = 0
# Enqueue operation
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]
Def is_empty(self):
Return self.count == 0
Def is_full(self):
Def display(self):
Print(“Queue:”, self.queue)
# Usage example
Queue = SimpleQueue(5)
Queue.enqueue(10)
Queue.enqueue(20)
Queue.enqueue(30)
Queue.display()
Queue.dequeue()
Queue.display()
Class CircularQueue:
Self.front = 0
Self.rear = -1
Self.capacity = size
Self.count = 0
# Enqueue operation
If self.is_full():
Print(“Queue is full”)
Return
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.count -= 1
Return removed
# Peek operation
Def peek(self):
If self.is_empty():
Print(“Queue is empty”)
Return None
Return self.queue[self.front]
Def is_empty(self):
Return self.count == 0
Def is_full(self):
Def display(self):
# Usage example
Cq = CircularQueue(5)
Cq.enqueue(1)
Cq.enqueue(2)
Cq.enqueue(3)
Cq.display()
Cq.display()
Class PriorityQueue:
Def __init__(self):
Self.queue = []
# Enqueue operation
Self.queue.append((item, priority))
# Dequeue operation
Def dequeue(self):
If self.is_empty():
Return None
# Peek operation
Def peek(self):
If self.is_empty():
Return None
Return self.queue[0]
# Check if the queue is empty
Def is_empty(self):
Return len(self.queue) == 0
Def display(self):
# Usage example
Pq = PriorityQueue()
Pq.display()
Print(“Dequeued:”, pq.dequeue())
Pq.display()
A deque allows inserting and removing elements from both the front and
rear.
Class Deque:
Self.rear = -1
Self.capacity = size
Self.count = 0
If self.is_full():
Print(“Deque is full”)
Return
Self.queue[self.front] = item
Self.count += 1
If self.is_full():
Print(“Deque is full”)
Return
Self.queue[self.rear] = item
Self.count += 1
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.count -= 1
Return removed
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.count -= 1
Return removed
Def peek_front(self):
If self.is_empty():
Print(“Deque is empty”)
Return None
Return self.queue[self.front]
Def peek_rear(self):
If self.is_empty():
Print(“Deque is empty”)
Return None
Return self.queue[self.rear]
Def is_empty(self):
Return self.count == 0
Def is_full(self):
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()
Dq.delete_front()
Dq.delete_rear()
Dq.display()
2. Circular Queue: Circular queues are ideal for scenarios like circular CPU
scheduling or managing buffers where old data needs to be
overwritten.
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.
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.
We’ll use the threading module and the Lock object to ensure that only one
thread can modify the queue at any time.
Import threading
Class ConcurrentQueue:
Self.front = 0
Self.rear = -1
Self.capacity = size
Self.count = 0
Self.lock = threading.Lock()
With self.lock:
If self.is_full():
Print(“Queue is full”)
Return
Self.queue[self.rear] = item
Self.count += 1
Print(f”Enqueued: {item}”)
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.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]
Def is_empty(self):
Return self.count == 0
Def is_full(self):
Def display(self):
With self.lock:
Print(“Queue:”, self.queue)
Import time
Import threading
Def producer(queue, items):
Queue.enqueue(item)
Time.sleep(0.1)
Def consumer(queue):
While True:
Queue.dequeue()
Time.sleep(0.2)
# Usage example
Queue = ConcurrentQueue(5)
Producer_thread.start()
Consumer_thread.start()
Producer_thread.join()
Time.sleep(1)
Explanation:
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).
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).
Thread Safety: Prevents data corruption when multiple threads try to access
the queue at the same time.
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).
Class SimpleQueue {
Capacity = size;
Front = 0;
Rear = -1;
Count = 0;
// Enqueue operation
If (isFull()) {
System.out.println(“Queue is full”);
Return;
Rear++;
Queue[rear] = item;
Count++;
System.out.println(“Enqueued: “ + item);
// Dequeue operation
If (isEmpty()) {
System.out.println(“Queue is empty”);
Return -1;
Rear--;
Count--;
Return removed;
// Peek operation
If (isEmpty()) {
System.out.println(“Queue is empty”);
Return -1;
Return queue[front];
Return count == 0;
}
// Display queue contents
System.out.print(“Queue: “);
System.out.print(queue[i] + “ “);
System.out.println();
Queue.enqueue(10);
Queue.enqueue(20);
Queue.enqueue(30);
Queue.display();
System.out.println(“Dequeued: “ + queue.dequeue());
Queue.display();
Class CircularQueue {
Capacity = size;
Front = 0;
Rear = -1;
Count = 0;
// Enqueue operation
If (isFull()) {
System.out.println(“Queue is full”);
Return;
Queue[rear] = item;
Count++;
System.out.println(“Enqueued: “ + item);
// Dequeue operation
If (isEmpty()) {
System.out.println(“Queue is empty”);
Return -1;
}
Count--;
Return removed;
// Peek operation
If (isEmpty()) {
System.out.println(“Queue is empty”);
Return -1;
Return queue[front];
Return count == 0;
System.out.println();
Queue.enqueue(10);
Queue.enqueue(20);
Queue.enqueue(30);
Queue.display();
System.out.println(“Dequeued: “ + queue.dequeue());
Queue.display();
Class PriorityQueue {
This.capacity = capacity;
Size = 0;
If (isFull()) {
Return;
Queue[size][0] = item;
Queue[size][1] = priority;
Size++;
sortByPriority();
// Swap
Queue[j + 1] = temp;
If (isEmpty()) {
Return -1;
Size--;
Return removed;
If (isEmpty()) {
Return -1;
}
Return queue[0][0];
Return size == 0;
System.out.println();
Pq.enqueue(10, 2);
Pq.enqueue(20, 1);
Pq.enqueue(30, 3);
Pq.display();
System.out.println(“Dequeued: “ + pq.dequeue());
Pq.display();
Class Deque {
Capacity = size;
Front = 0;
Rear = -1;
Count = 0;
If (isFull()) {
System.out.println(“Deque is full”);
Return;
Deque[front] = item;
Count++;
If (isFull()) {
System.out.println(“Deque is full”);
Return;
Deque[rear] = item;
Count++;
If (isEmpty()) {
System.out.println(“Deque is empty”);
Return -1;
}
Int removed = deque[front];
Count--;
Return removed;
If (isEmpty()) {
System.out.println(“Deque is empty”);
Return -1;
Count--;
Return removed;
If (isEmpty()) {
System.out.println(“Deque is empty”);
Return -1;
Return deque[front];
}
// Peek at the rear
If (isEmpty()) {
System.out.println(“Deque is empty”);
Return -1;
Return deque[rear];
Return count == 0;
System.out.print(“Deque: “);
System.out.println();
}
Deque.insertRear(10);
Deque.insertRear(20);
Deque.insertRear(30);
Deque.display();
Deque.insertFront(40);
Deque.insertFront(50);
Deque.display();
Deque.display();
Deque.display();
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.
Inserts an element at the rear (back) of the deque. This is done by moving
the rear pointer forward circularly.
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.
These operations return the elements at the front and rear, respectively,
without modifying the queue.
6. Display:
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.