DS Unit4
DS Unit4
Subject Name
Data Structures
Prepared by:
Introduction to Queues:
What is a Queue?
Queue is the data structure that is similar to the queue in the real world.
A queue is a data structure in which whatever comes first will go out first, and it follows the FIFO
(First-In-First-Out) policy.
Queue can also be defined as the list or collection in which the insertion is done from one end known
as the rear end or the tail of the queue
whereas the deletion is done from another end known as the front end or the head of the queue.
The real-world example of a queue is the ticket queue outside a cinema hall, where the person who enters first in
the queue gets the ticket first, and the last person enters in the queue gets the ticket at last. Similar approach is
followed in the queue in data structure.
Example:
Queue Representation:
Types of Queue
There are four different types of queue that are listed as follows –
Input Restricted Queue
Output Restricted Queue
Simple Queue or Linear Queue
Circular Queue
Priority Queue
Double Ended Queue (or Deque)
o 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.
o 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.
o 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.
o Double-Ended Queue (Dequeue): In a double-ended queue the insertion and deletion operations,
both can be performed from both ends.
o Priority Queue: A priority queue is a special queue where the elements are accessed based on the
priority assigned to them.
The major drawback of using a linear Queue is that insertion is done only from the rear end.
If the first three elements are deleted from the Queue, we cannot insert more elements even though the
space is available in a Linear Queue.
In this case, the linear Queue shows the overflow condition as the rear is pointing to the last element of
the Queue.
Circular Queue
In Circular Queue, all the nodes are represented as circular.
It is similar to the linear Queue except that the last element of the queue is connected to the first
element.
It is also known as Ring Buffer, as all the ends are connected to another end.
The drawback that occurs in a linear queue is overcome by using the circular queue.
If the empty space is available in a circular queue, the new element can be added in an empty space by
simply incrementing the value of rear.
The main advantage of using the circular queue is better memory utilization.
Priority Queue
It is a special type of queue in which the elements are arranged based on the priority.
It is a special type of queue data structure in which every element has a priority associated with it.
Suppose some elements occur with the same priority, they will be arranged according to the FIFO
principle.
Insertion in priority queue takes place based on the arrival, while deletion in the priority queue occurs
based on the priority.
Priority queue is mainly used to implement the CPU scheduling algorithms.
There are two types of priority queue that are discussed as follows -
o Ascending priority queue - In ascending priority queue, elements can be inserted in arbitrary order,
but only smallest can be deleted first. Suppose an array with elements 7, 5, and 3 in the same order, so,
insertion can be done with the same sequence, but the order of deleting the elements is 3, 5, 7.
o Descending priority queue - In descending priority queue, elements can be inserted in arbitrary order,
but only the largest element can be deleted first. Suppose an array with elements 7, 3, and 5 in the same
order, so, insertion can be done with the same sequence, but the order of deleting the elements is 7, 5,
o Output restricted deque - As the name implies, in output restricted queue, deletion operation can be
performed at only one end, while insertion can be performed from both ends.
1. Enqueue():
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.
Enqueue representation
Implementation of Enqueue:
// 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);
}
2. Dequeue():
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.
Dequeue operation
Implementation of dequeue:
// 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;
}
3. front():
This operation returns the element at the front end without removing it.
// Function to get front of queue
int front(struct Queue* queue)
{
if (isempty(queue))
return INT_MIN;
return queue->arr[queue->front];
}
4. rear():
This operation returns the element at the rear end without removing it.
int rear(struct Queue* front)
{
if (front == NULL) {
printf("Queue is empty.\n");
return -1;
}
return front->data;
}
5. isEmpty():
This operation returns a boolean value that indicates whether the queue is empty or not.
C
/ Queue is full when size becomes
// equal to the capacity
bool isFull(struct Queue* queue)
{
return (queue->size == queue->capacity);
}bool isEmpty(struct Queue* queue)
{
return (queue->size == 0);
}
6. isFull():
This operation returns a boolean value that indicates whether the queue is full or not.
// Queue is full when size becomes
// equal to the capacity
bool isFull(struct Queue* queue)
{
return (queue->size == queue->capacity);
}
Characteristics of Queue:
Queue can handle multiple data.
We can access both ends.
They are fast and flexible.
We can easily represent queue by using linear arrays. There are two variables i.e. front and rear, that are
implemented in the case of every queue. Front and rear variables point to the position from where insertions
and deletions are performed in a queue. Initially, the value of front and queue is -1 which represents an empty
queue. Array representation of a queue containing 5 elements along with the respective values of front and rear,
is shown in the following figure.
The above figure shows the queue of characters forming the English word "HELLO". Since, No deletion is
performed in the queue till now, therefore the value of front remains -1 . However, the value of rear increases
by one every time an insertion is performed in the queue. After inserting an element into the queue shown in the
above figure, the queue will look something like following. The value of rear will become 5 while the value of
front remains same.
After deleting an element, the value of front will increase from -1 to 0. however, the queue will look something
like following.
Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow error.
If the item is to be inserted as the first element in the list, in that case set the value of front and rear to 0 and
insert the element at the rear end.
Otherwise keep increasing the value of rear and insert each element one by one having rear as the index.
Algorithm
o Step1: IFREAR=MAX-1
WriteOVERFLOW
Gotostep
[END OF IF]
o Step2: IFFRONT=-1andREAR=-1
SETFRONT=REAR=0
ELSE
SETREAR=REAR+1
[END OF IF]
o Step 3: Set QUEUE[REAR] = NUM
o Step 4: EXIT
C Function
void insert (int queue[], int max, int front, int rear, int item)
{
if (rear + 1 == max)
{
printf("overflow");
}
else
{
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear + 1;
}
queue[rear]=item;
}
}
If, the value of front is -1 or value of front is greater than rear , write an underflow message and exit.
Otherwise, keep increasing the value of front and return the item stored at the front end of the queue at
each time.
Algorithm
o Step1: IFFRONT=-1orFRONT>REAR
WriteUNDERFLOW
ELSE
SETVAL=QUEUE[FRONT]
SETFRONT=FRONT+1
[END OF IF]
o Step 2: EXIT
C Function
int delete (int queue[], int max, int front, int rear)
{
int y;
if (front == -1 || front > rear)
{
printf("underflow");
}
else
{
y = queue[front];
if(front == rear)
{
front = rear = -1;
else
front = front + 1;
}
return y;
}
}
#include<stdio.h>
#include<stdlib.h>
#define maxsize 5
void insert();
void delete();
void display();
int front = -1, rear = -1;
int queue[maxsize];
void main ()
{
int choice;
while(choice != 4)
{
printf("\n*************************Main Menu*****************************\n");
printf("\n=================================================================\n");
printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
printf("\nEnter your choice ?");
scanf("%d",&choice);
switch(choice)
{
case 1: insert();
break;
case 2: delete();
break;
case 3: display();
break;
case 4: exit(0);
break;
default: printf("\nEnter valid choice??\n");
}
}
}
void insert()
{
int item;
Course Lecturer: Ms. K. SaiDivya MJR College of Engineering and Technology-Piler
10
I-B.Tech (Common for all branches) Course Name: Introduction to Programming
printf("\nEnter the element\n");
scanf("\n%d",&item);
if(rear == maxsize-1)
{
printf("\nOVERFLOW\n");
return;
}
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear+1;
}
queue[rear] = item;
printf("\nValue inserted ");
}
void delete()
{
int item;
if (front == -1 || front > rear)
{
printf("\nUNDERFLOW\n");
return;
}
else
{
item = queue[front];
if(front == rear)
{
front = -1;
rear = -1 ;
}
else
{
front = front + 1;
}
printf("\nvalue deleted ");
}
}
void display()
{
int i;
if(rear == -1)
{
printf("\nEmpty queue\n");
}
else
{ printf("\nprinting values .....\n");
for(i=front;i<=rear;i++)
{
printf("\n%d\n",queue[i]);
}
}
}
Output:
*************************Main Menu*****************************
=================================================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?1
Enter the element
123
Value inserted
*************************Main Menu*****************************
=================================================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?1
Enter the element
90
Value inserted
*************************Main Menu*****************************
=================================================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?2
value deleted
*************************Main Menu*****************************
=================================================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?3
printing values .....
90
*************************Main Menu*****************************
=================================================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?
The front pointer contains the address of the starting element of the queue while the rear pointer
contains the address of the last element of the queue.
Insertion and deletions are performed at rear and front end respectively. If front and rear both are
NULL, it indicates that the queue is empty.
There are two basic operations which can be implemented on the linked queues. The operations are Insertion
and Deletion.
Insert operation
The insert operation append the queue by adding an element to the end of the queue.
The new element will be the last element of the queue.
Firstly, allocate the memory for the new node ptr by using the following statement.
There can be the two scenario of inserting this new node ptr into the linked queue.
In the first scenario, we insert element into an empty queue. In this case, the condition front = NULL becomes
true. Now, the new element will be added as the only element of the queue and the next pointer of front and rear
pointer both, will point to NULL.
In the second case, the queue contains more than one element. The condition front = NULL becomes false. In
this scenario, we need to update the end pointer rear so that the next pointer of rear will point to the new node
ptr. Since, this is a linked queue, hence we also need to make the rear pointer point to the newly added node ptr.
We also need to make the next pointer of rear point to NULL.
Algorithm
C Function
Deletion Operation:
Deletion operation removes the element that is first inserted among all the queue elements.
Firstly, we need to check either the list is empty or not.
The condition front == NULL becomes true if the list is empty, in this case , we simply write
underflow on the console and make exit.
Otherwise, we will delete the element that is pointed by the pointer front.
For this purpose, copy the node pointed by the front pointer into the pointer ptr. Now, shift the front pointer,
point to its next node and free the node pointed by the node ptr. This is done by using the following statements.
ptr = front;
front = front -> next;
free(ptr);
Algorithm
o Step1: IFFRONT=NULL
Write"Underflow"
GotoStep5
[END OF IF]
o Step 2: SET PTR = FRONT
o Step 3: SET FRONT = FRONT -> NEXT
o Step 4: FREE PTR
o Step 5: END
C Function
1. void delete (struct node *ptr)
2. {
3. if(front == NULL)
4. {
5. printf("\nUNDERFLOW\n");
6. return;
7. }
8. else
9. {
10. ptr = front;
11. front = front -> next;
12. free(ptr);
13. }
14. }
71. }
72. }
73. void delete ()
74. {
75. struct node *ptr;
76. if(front == NULL)
77. {
78. printf("\nUNDERFLOW\n");
79. return;
80. }
81. else
82. {
83. ptr = front;
84. front = front -> next;
85. free(ptr);
86. }
87. }
88. void display()
89. {
90. struct node *ptr;
91. ptr = front;
92. if(front == NULL)
93. {
94. printf("\nEmpty queue\n");
95. }
96. else
97. { printf("\nprinting values .....\n");
98. while(ptr != NULL)
99. {
100. printf("\n%d\n",ptr -> data);
101. ptr = ptr -> next;
102. }
103. }
104. }
Output:
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter your choice ?1
Enter value?
123
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Enter value?
90
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
123
90
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
90
***********Main Menu**********
==============================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Applications of Queue:
Multi programming: Multi programming means when multiple programs are running in the main
memory.
Network: In a network, a queue is used in devices such as a router or a switch. another application
of a queue is a mail queue which is a directory that stores data and controls files for mail messages.
Job Scheduling: The computer has a task to execute a particular number of jobs that are scheduled
to be executed one after another. These jobs are assigned to the processor one by one which is
organized using a queue.
Task Scheduling: Queues can be used to schedule tasks based on priority or the order in which they
were received.
Resource Allocation: Queues can be used to manage and allocate resources, such as printers or
CPU processing time.
Event Handling: Queues can be used to handle events in event-driven systems, such as GUI
applications or simulation systems.
Traffic Management: Queues can be used to manage traffic flow in transportation systems, such as
airport control systems or road networks.
Operating systems: Operating systems often use queues to manage processes and resources. For
example, a process scheduler might use a queue to manage the order in which processes are
executed.
Printer queues :In printing systems, queues are used to manage the order in which print jobs are
processed. Jobs are added to the queue as they are submitted, and the printer processes them in the
order they were received.
Web servers: Web servers use queues to manage incoming requests from clients. Requests are
added to the queue as they are received, and they are processed by the server in the order they were
received.
Breadth-first search algorithm: The breadth-first search algorithm uses a queue to explore nodes
in a graph level-by-level. The algorithm starts at a given node, adds its neighbors to the queue, and
then processes each neighbor in turn.
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.
In a classical queue, a new element can only be inserted when the existing elements are deleted from the
queue.
Searching an element takes O(N) time.
Maximum size of a queue must be defined prior in case of array implementation.
Example of BFS
Now, let's understand the working of BFS algorithm by using an example. In the example given below, there is
a directed graph having 7 vertices.
In the above graph, minimum path 'P' can be found by using the BFS that will start from Node A and end at
Node E.
Types of deque
In output restricted queue, deletion operation can be performed at only one end, while insertion can be
performed from both ends.
In this operation, the element is inserted from the front end of the queue. Before implementing the operation, we
first have to check whether the queue is full or not. 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.
In this operation, the element is inserted from the rear end of the queue. Before implementing the operation, we
first have to check again whether the queue is full or not. If the queue is not full, then the element can be
inserted from the rear 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, 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.
In this operation, the element is deleted from the front end of the queue. Before implementing the operation, we
first have to check whether the queue is empty or not.
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
Else if front is at end (that means front = size - 1), set front = 0.
In this operation, the element is deleted from the rear end of the queue. Before implementing the operation, we
first have to check whether the queue is empty or not.
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.
Check empty
This operation is performed to check whether the deque is empty or not. If front = -1, it means that the deque is
empty.
Check full
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.
Applications of deque
It is used in job scheduling algorithms.
It supports both stack and queue operations.
The clockwise and anti-clockwise rotation operations in deque are performed in O(1) time which is
helpful in many problems.
Advantages of Deque:
You are able to add and remove items from the both front and back of the queue.
Deques are faster in adding and removing the elements to the end or beginning.
The clockwise and anti-clockwise rotation operations are faster in a deque.
Dynamic Size: Deques can grow or shrink dynamically.
Efficient Operations: Deques provide efficient O(1) time complexity for inserting and removing elements
from both ends.
Versatile: Deques can be used as stacks (LIFO) or queues (FIFO), or as a combination of both.
No Reallocation: Deques do not require reallocation of memory when elements are inserted or removed.
Thread Safe: Deques can be thread-safe if used with proper synchronization.
Cache-Friendly: Deques have a contiguous underlying storage structure which makes them cache-
friendly.
Disadvantages of Deque:
Deque has no fixed limitations for the number of elements they may contain. This interface supports
capacity-restricted deques as well as the deques with no fixed size limit.
They are less memory efficient than a normal queue.
Memory Overhead: Deques have higher memory overhead compared to other data structures due to the
extra pointers used to maintain the double-ended structure.
Synchronization: Deques can cause synchronization issues if not used carefully in multi-threaded
environments.
Complex Implementation: Implementing a deque can be complex and error-prone, especially if
implementing it manually.
Not All Platforms: Deques may not be supported by all platforms, and may need to be implemented
manually.
Not Suitable for Sorting: Deques are not designed for sorting or searching, as these operations require
linear time.
Limited Functionality: Deques have limited functionality compared to other data structures such as
arrays, lists, or trees.