FDS Unit 6 Notes
FDS Unit 6 Notes
Unit –VI
Queue
1. Queue
A Queue Data Structure is a fundamental concept in computer science used for storing and
managing data in a specific order. It follows the principle of "First in, First out" (FIFO),
where the first element added to the queue is the first one to be removed. Queues are commonly
used in various algorithms and applications for their simplicity and efficiency in managing data
flow.
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.
2|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
The queue abstract data type (ADT) follows the basic design of the stack abstract data type.
Each node contains a void pointer to the data and the link pointer to the next element in the
queue. The program’s responsibility is to allocate memory for storing the data.
dequeue() – Remove and return the first element of the queue, if the queue is not empty.
peek() – Return the element of the queue without removing it, if the queue is not empty.
1. enqueue(value): This operation adds an element to the rear (end) of the queue.
Steps:
If the rear index points to the last position in the array and the queue is full,
insertion is not allowed.
2. If the queue is empty (both front and rear are -1), set front = 0 and rear = 0.
3. If not empty, increment the rear and insert the element at the new position.
2. dequeue(): This operation removes an element from the front of the queue.
Steps:
3|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
4. If after deletion, front > rear, reset front and rear to -1 (queue is empty).
3. peek(): This operation retrieves the element at the front of the queue without removing it.
Steps:
Steps:
5. isFull(): This operation checks whether the queue has reached its maximum size (only
for array-based implementation).
Steps:
A queue can be efficiently represented using a linear array. Two variables, front and rear, are
used to keep track of the positions where elements are deleted and inserted, respectively.
2. After Inserting the First Element: When the first element is inserted:
Both front and rear are set to 0 (pointing to the same index, where the first element is
stored).
rear increments with each insertion, pointing to the latest added element.
front remains unchanged until a deletion occurs.
Example: Pseudo C++ Code for Linear Queue Implementation Using Array
public:
// Constructor to initialize the queue
LinearQueue(int capacity) {
size = capacity;
queue = new int[size];
front = -1;
rear = -1;
}
if (isEmpty()) {
print("Queue is empty! No front element.");
return -1; // Indicate failure
}
return queue[front];
}
// Example Usage
void main() {
LinearQueue queue(5); // Create a queue with capacity 5
queue.enqueue(10); // Add elements
queue.enqueue(20);
print(queue.peek()); // Output: 10
print(queue.dequeue()); // Output: 10
print(queue.dequeue()); // Output: 20
print(queue.dequeue()); // Output: Queue Underflow!
}
1.5 Circular Queue and its advantages
A Circular Queue is an extended version of a normal queue where the last element of the queue
is connected to the first element of the queue forming a circle.
The operations are performed based on FIFO (First In First Out) principle. It is also
called ‘Ring Buffer’.
8|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
If the queue is full (i.e., (rear + 1) % size == front), the operation cannot be performed,
and an overflow condition occurs.
2. dequeue(): This operation removes the front element from the queue.
9|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
If the queue is empty (i.e., front == rear), the operation cannot be performed, and an
underflow condition occurs.
3. peek(): This operation allows you to view the front element without removing it.
4. isFull(): This operation checks whether the queue has reached its maximum capacity.
There was one limitation in the array implementation of Queue. If the rear reaches to the end
position of the queue, then there might be possibility that some vacant spaces are left in the
beginning which cannot be utilized. So, to overcome such limitations, the concept of the
circular queue was introduced.
1. No Space Wastage:
Circular Queue: The front and rear pointers can wrap around, reusing previously
emptied spaces.
Linear Queue: Once the front reaches the end, no further enqueuing is possible without
resetting.
2. Reduced Overhead:
Circular Queue: Ideal for buffer management, round-robin scheduling, and continuous
operations.
Linear Queue: Less efficient when elements are continuously added or removed.
4. Prevents Overflow:
Linear Queue: Can overflow even with unused space due to fixed rear position.
1.1 Multi-queues
Multi Queues refer to the concept of using multiple queues to manage different sets of data,
typically when tasks or resources are divided into several categories. Each queue is used to
manage a specific type of data or task, and they work independently of each other.
Key Points:
1. Multiple Queues: A system may implement several queues instead of just one, to
handle different types of operations or resources (e.g., one queue for high-priority tasks,
another for low-priority tasks).
2. Shared Storage: Although the queues are separate, they may share a common memory
space (array or linked list) to manage the queues, often by dividing the space into
segments.
3. Usage: Multi-queues are useful in situations where different kinds of data need to be
processed at different rates or with different priorities. For example, in operating
systems, you could have separate queues for different levels of task priorities or
different processes.
Example:
Tasks are processed based on their priority by dequeuing from the respective queue.
Advantage:
A queue is generally implemented using an array, but the limitation of this kind of queue is that
the memory occupied by the array is fixed no matter how many elements are in the queue. In
15 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
the queue implemented using a linked list, the size occupied by the linked list will be equal to
the number of elements in the queue. Moreover, its size is dynamic, meaning that the size will
change automatically according to the elements present.
Basic Operations of Linear Queue using Singly Linked List (with isFull)
A linear queue implemented using a singly linked list follows the FIFO (First-In-First-Out)
principle. The basic operations are:
If the queue is empty, both front and rear pointers are set to the new node.
Otherwise, the next pointer of the current rear node is updated to point to the new
node, and the rear pointer is updated to the new node.
The element at the front is removed, and the front pointer is updated to the next
node.
3. peek(): Returns the data of the front element of the queue without removing it.
4. isEmpty(): Checks if the queue is empty by checking if the front pointer is nullptr.
In a singly linked list implementation, the queue size is dynamic and does not have a
fixed limit, so it is never full as long as there is available memory.
Dynamic Size: Unlike arrays, the size of the queue is not fixed.
Efficient Memory Usage: The queue grows and shrinks as needed, using memory only
for the elements in the queue.
if (isEmpty()) {
front = newNode; // If the queue is empty, front points to
the new node
} else {
rear->next = newNode; // Link the current rear to the new
node
}
rear = newNode; // Move the rear pointer to the new node
}
if (front == nullptr) {
rear = nullptr; // If the queue is empty, reset the rear
pointer
}
return dequeuedValue;
}
// Example usage
void main() {
enqueue(10); // Enqueue 10 to the queue
enqueue(20); // Enqueue 20 to the queue
print(peek()); // Output: 10 (front of the queue)
print(dequeue()); // Output: 10 (removed from the queue)
print(dequeue()); // Output: 20 (removed from the queue)
print(dequeue()); // Output: Queue Underflow!
}
2. Deque
The deque stands for Double Ended Queue. Deque is a linear data structure where the insertion
and deletion operations are performed from both ends. We can say that deque is a generalized
version of the queue.
Though the insertion and deletion in a deque can be performed on both ends, it does not follow
the FIFO rule. The representation of a deque is given as follows –
Operations on a Deque
1. insertFront(): In this operation, the element is inserted from the front end of the queue.
If the queue is not full, then the element can be inserted from the front end by using the below
conditions -
o If the queue is empty, both rear and front are initialized with 0. Now, both will point to
the first element.
o Otherwise, check the position of the front if the front is less than 1 (front < 1), then
reinitialize it by front = n - 1, i.e., the last index of the array.
2. insertRear(): In this operation, the element is inserted from the rear end of the queue
If the queue is not full, then the element can be inserted from the rear end by using the below
conditions -
20 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
o If the queue is empty, both rear and front are initialized with 0. Now, both will point to
the first element.
o Otherwise, increment the rear by 1. If the rear is at last index (or size - 1), then instead
of increasing it by 1, we have to make it equal to 0.
3. deleteFront(): In this operation, the element is deleted from the front end of the queue.
If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot perform
the deletion. If the queue is not full, then the element can be inserted from the front end by
using the below conditions -
If the deque has only one element, set rear = -1 and front = -1.
Else if front is at end (that means front = size - 1), set front = 0.
Else increment the front by 1, (i.e., front = front + 1).
4. deleteRear(): In this operation, the element is deleted from the rear end of the queue.
21 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot perform
the deletion.
If the deque has only one element, set rear = -1 and front = -1.
5. isEmpty(): This operation is performed to check whether the deque is empty or not.
6. isFull(): This operation is performed to check whether the deque is full or not.
If front = rear + 1, or front = 0 and rear = n - 1 it means that the deque is full.
The time complexity of all of the above operations of the deque is O(1), i.e., constant.
if (isEmpty()) {
front = rear = newNode; // If empty, both front and rear
point to the new node
} else {
front->prev = newNode; // Link the previous front to the
new node
front = newNode; // Update front to the new node
}
}
if (isEmpty()) {
front = rear = newNode; // If empty, both front and rear
point to the new node
23 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
} else {
rear->next = newNode; // Link the current rear to the new
node
rear = newNode; // Update rear to the new node
}
}
if (front == nullptr) {
rear = nullptr; // If the dequeue is empty, reset the rear
pointer
} else {
front->prev = nullptr; // Disconnect the front node
}
if (rear == nullptr) {
front = nullptr; // If the dequeue is empty, reset the front
pointer
} else {
rear->next = nullptr; // Disconnect the rear node
}
// Example usage
void main() {
insertFront(10); // Insert 10 at the front
insertRear(20); // Insert 20 at the rear
insertFront(5); // Insert 5 at the front
displayDequeue(); // Display all elements in dequeue
print(deleteFront()); // Output: 5 (deleted from front)
print(deleteRear()); // Output: 20 (deleted from rear)
displayDequeue(); // Display remaining elements
}
An input restricted queue is a special case of a double-ended queue where data can be inserted
from one end(rear) but can be removed from both ends (front and rear). This kind of Queue
does not follow FIFO (first in first out):
Mainly the following three basic operations are performed on input restricted queue:
Example: Pseudo C++ Code for Insertion and Deletion in Input Restricted Deque
// If the deque is empty, both front and rear are the new node
if (isEmpty()) {
front = rear = newNode;
} else {
27 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
// Example usage
void main() {
insertRear(10); // Insert 10 at the rear
insertRear(20); // Insert 20 at the rear
print(deleteFront()); // Output: 10
print(deleteRear()); // Output: 20
}
2.3 Output restricted Queue
29 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Mainly the following three basic operations are performed on output restricted queue:
In addition to the above operations, the following operations are also supported
Example: Pseudo C++ Code for Insertion and Deletion in Output Restricted Deque
// If the deque is empty, both front and rear are the new node
if (isEmpty()) {
front = rear = newNode;
} else {
front = newNode;
}
}
// If the deque is empty, both front and rear are the new node
if (isEmpty()) {
front = rear = newNode;
} else {
31 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
// Example usage
void main() {
insertFront(10); // Insert 10 at the front
insertRear(20); // Insert 20 at the rear
print(deleteFront()); // Output: 10
32 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
print(deleteFront()); // Output: 20
}
3. Priority Queue
3.1 Basic concept
A priority queue is a type of queue that arranges elements based on their priority values.
Elements with higher priority values are typically retrieved or removed before elements with
lower priority values. Each element has a priority value associated with it. When we add an
item, it is inserted in a position based on its priority value.
The hospital emergency queue is an ideal real-life example of a priority queue. In this queue
of patients, the patient with the most critical situation is the first in a queue, and the patient who
doesn’t need immediate medical attention will be the last. In this queue, the priority depends
on the medical condition of the patients.
Now, we will see how to represent the priority queue through a one-way list.
We will create the priority queue by using the list given below in which INFO list contains the
data elements, PRN list contains the priority numbers of each data element available in
the INFO list, and LINK basically contains the address of the next node.
33 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
In the case of priority queue, lower priority number is considered the higher priority,
i.e., lower priority number = higher priority.
Step 1: In the list, lower priority number is 1, whose data value is 300, so it will be inserted in
the list as shown in the below diagram:
Step 2: After inserting 300, priority number 2 has a higher priority, and data values associated
with this priority are 100 and 200. So, this data will be inserted based on the FIFO principle;
therefore 200 will be added first and then 100.
Step 3: After inserting the elements of priority 2, the next higher priority number is 4 and data
elements associated with 4 priority numbers are 400, 500, 700. In this case, elements would be
inserted based on the FIFO principle; therefore, 400 will be added first, then 500, and then 700.
Step 4: After inserting the elements of priority 4, the next higher priority number is 5, and the
value associated with priority 5 is 600, so it will be inserted at the end of the queue.
In the Array-based implementation, the queue is represented as an array, and we manage the
insertion and deletion based on the priority of the elements.
34 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
2. delete(): Remove and return the element with the highest priority.
3. peek(): View the element with the highest priority without removing it.
5. isFull(): Check if the priority queue is full (in case of a fixed size array).
struct Element {
int data; // Data of the element
int priority; // Priority of the element
};
if (isFull()) {
print("Queue Overflow! Cannot insert.");
return;
}
rear++;
int i = rear;
// Shift elements with lower priority to make space for the new
element
while (i > 0 && pq[i - 1].priority < priority) {
pq[i] = pq[i - 1];
i--;
}
pq[i].data = element;
pq[i].priority = priority;
}
1. Ascending order priority queue: In ascending order priority queue, a lower priority number
is given as a higher priority in a priority. For example, we take the numbers from 1 to 5 arranged
in an ascending order like 1,2,3,4,5; therefore, the smallest number, i.e., 1 is given as the highest
priority in a priority queue.
2. Descending order priority queue: In descending order priority queue, a higher priority
number is given as a higher priority in a priority. For example, we take the numbers from 1 to
5 arranged in descending order like 5, 4, 3, 2, 1; therefore, the largest number, i.e., 5 is given
as the highest priority in a priority queue.