0% found this document useful (0 votes)
27 views149 pages

Dsa Unit-3

The document provides a comprehensive overview of stack data structures, including their definition, operations (push, pop, peek, isFull, isEmpty), and algorithms for converting infix expressions to postfix. It explains how to evaluate postfix expressions using stacks and includes examples and pseudo code for implementation. Additionally, it covers the principles of LIFO and the significance of stack operations in programming.

Uploaded by

Jatin Pochiraju
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views149 pages

Dsa Unit-3

The document provides a comprehensive overview of stack data structures, including their definition, operations (push, pop, peek, isFull, isEmpty), and algorithms for converting infix expressions to postfix. It explains how to evaluate postfix expressions using stacks and includes examples and pseudo code for implementation. Additionally, it covers the principles of LIFO and the significance of stack operations in programming.

Uploaded by

Jatin Pochiraju
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 149

Operation on Stack

ADT
DEEPAJOTHI
What is a Stack?
• A stack is a linear data structure where elements are
stored in the LIFO (Last In First Out) principle where the
last element inserted would be the first element to be
deleted. A stack is an Abstract Data Type (ADT), that is
popularly used in most programming languages. It is
named stack because it has the similar operations as the
real-world stacks, for example − a pack of cards or a pile
of plates, etc.
Stack Representation
• A stack allows all data
operations at one end
only. At any given
time, we can only
access the top element
of a stack.
• The following diagram
depicts a stack and its
operations −
Basic Operations on Stacks
• Stack operations are usually performed for
initialization, usage and, de-initialization of the stack
ADT.
• The most fundamental operations in the stack ADT
include: push(), pop(), peek(), isFull(), isEmpty().
These are all built-in operations to carry out data
manipulation and to check the status of the stack.
• Stack uses pointers that always point to the topmost
element within the stack, hence called as
the top pointer.
Stack Insertion: push()

The push() is an operation that inserts elements into the


stack.
• 1. Checks if the stack is full.
• 2. If the stack is full, produces an error and exit.
• 3. If the stack is not full, increments top to point next
empty space.
• 4. Adds data element to the stack location, where top is
pointing.
• 5. Returns success.
• #include <stdio.h>
/* Main function */
int MAXSIZE = 8;
int stack[8]; int main(){
int top = -1; int i;
/* Check if the stack is full*/ push(44);
int isfull(){ push(10);
if(top == MAXSIZE) push(62);
return 1;
push(123);
else
return 0; } push(15);
/* Function to insert into the stack */ printf("Stack Elements: \n");
int push(int data){ // print stack data
if(!isfull()) { for(i = 0; i < 8; i++) {
top = top + 1; printf("%d ", stack[i]); }
stack[top] = data;
return 0;}
} else {
printf("Could not insert data, Stack is full.\n"); Output:
}}} Stack Elements: 44 10 62 123 15 0 0 0
Stack Deletion: pop()

• The pop() is a data manipulation operation which removes elements


from the stack. The following pseudo code describes the pop()
operation in a simpler way.
• Algorithm:
• 1. Checks if the stack is empty.
• 2. If the stack is empty, produces an error and exit.
• 3. If the stack is not empty, accesses the data element at
• which top is pointing.
• 4. Decreases the value of top by 1.
• 5. Returns success.
#include <stdio.h> /* Function to delete from thestack*/ /* Main function*/
int MAXSIZE = 8; int pop(){ int main(){
int stack[8]; int data; int i;
int top = -1; if(!isempty()) { push(44);push(10);
/* Check if the stack is empty */ data = stack[top]; push(62);push(123);
int isempty(){ top = top - 1; push(15);
if(top == -1) return data; printf("Stack Elements: \
return 1; } else { // print stack data
else printf("Could not retrieve data, for(i = 0; i < 8; i++) {
return 0; Stack is empty.\n"); printf("%d ", stack[i]);
} }} }
/* Check if the stack is full*/ /* Function to insert into the stack */ /*printf("Element at top
int isfull(){ int push(int data){ printf("\nElements poppe
if(top == MAXSIZE) if(!isfull()) { // print stack data
return 1; top = top + 1; while(!isempty()) {
else stack[top] = data; int data = pop();
return 0; } else { printf("%d ",data);
} printf("Could not insert data, Stack } return 0;
Output
Stack Elements: 44 10 62 123 15 0 0 0
Elements popped: 15 123 62 10 44
Retrieving topmost Element from Stack: peek()

• The peek() is an operation retrieves the topmost


element within the stack, without deleting it. This
operation is used to check the status of the stack with
the help of the top pointer.
Algorithm

• 1. START
• 2. return the element at the top of the stack
• 3. END
#include <stdio.h>
/* Main function */
int MAXSIZE = 8; /* Function to insert into the stack int main(){
int stack[8]; */ int i;
push(44);
int top = -1; int push(int data){ push(10);
/* Check if the stack is full */ if(!isfull()) { push(62);
push(123);
int isfull(){ top = top + 1; push(15);
if(top == MAXSIZE) stack[top] = data; printf("Stack Elements: \n");
// print stack data
return 1; } else { for(i = 0; i < 8; i++) {
else printf("Could not insert data, printf("%d ", stack[i]);
Stack is full.\n"); }
return 0;} printf("\nElement at top of the s
/* Function to return the topmost } %d\n" ,peek());
element in the stack */ } return 0;
}
int peek(){
return stack[top]; Output
Stack Elements: 44 10 62 123 15 0 0 0 Element at
} top of the stack: 15
Verifying whether the Stack is full: isFull()
• The isFull() operation checks whether the stack is full. This
operation is used to check the status of the stack with the
help of top pointer.
• Algorithm
1. START
2. If the size of the stack is equal to the top position of the
stack,
the stack is full. Return 1.
3. Otherwise, return 0.
4. END
#include <stdio.h>
int MAXSIZE = 8; /* Main function */
int stack[8]; int main(){
int top = -1; printf("Stack full: %s\n" ,
/* Check if the stack is full */ isfull()?"true":"false");
int isfull(){ return 0;
if(top == MAXSIZE) }
return 1;
else Output
return 0; Stack full: false
}
Verifying whether the Stack is empty:
isEmpty()
• The isEmpty() operation verifies whether the stack is empty.
This operation is used to check the status of the stack with
the help of top pointer
• Algorithm
1. START
2. If the top value is -1, the stack is empty. Return 1.
3. Otherwise, return 0.
4. END.
#include <stdio.h>
int MAXSIZE = 8; /* Main function */
int stack[8]; int main() {
int top = -1; printf("Stack empty: %s\n" , i
/* Check if the stack is empty return 0;
*/ }
int isempty() {
if(top == -1)
return 1; Output
else Stack empty: true
THANK YOU
Convert Infix to Postfix
Expression Using Stack
Infix Expression

• This is more human-readable and generally used by humans for better


understanding, In this representation, the operator remains in the middle
and operand exists on both sides of the operator and the value can be
evaluated simply according to precedence and associativity rule.
Structure:
Operand1 Operator Operand2
Example:
• 5 + 4 * (9 - 2)
Explanation:
• 5 + 4 * (9 - 2) = 5 + 4 * 7 = 5 + 28 = 32
Postfix Expression
• In this notation, the operator remains on the right side of both
operands like this,
Structure:
• Operand1 Operand2 Operator
Example:
•5492-*+
Explanation:
• We can consider the expression (4 9 2 - *) before the last
operator as one operand and the first (5) as another operand.
Solving the expression recursively, 5 4 9 2 - * + = 5 4 7 * + =
5 28 + = 32
Prefix Expression
• Similar to the last one, in this notation the operator is on the
left side of both operands like this,
Structure:
• Operator Operand1 Operand2
Example:
+5*4-92
Explanation:
• Wthe operands for first + operator are, 5 and *4-92. This can
be solved by reading the expression from right to left.
+ 5 * 4 - 9 2 = + 5 * 4 7 = + 5 28 = 32
Algorithm to Convert Infix to
Postfix Expression Using Stack
Convert Infix to Postfix Expression Using
Stack
• The Infix Notation is traversed from left to right
and a stack is used here to maintain the operator
to be inserted in the postfix expression.
Algorithm
1.If we encounter an opening parenthesis (, we push it
into the stack.
2. If we have a closing parenthesis ), then keep popping
out elements from the top of the stack and append them
to the postfix expression until the top of the stack
contains (. In the end, just pop out the opening
parenthesis but make sure to not append this in the
postfix expression.
Algorithm
3. If we encounter an operand, then append it to the postfix
expression.
4. If we encounter any operator, then exist exists several cases,
• If the top of the stack contains the operator with less precedence in
comparison to the operator which is going to be pushed there or stack is
empty, then just push the new operator in the tack.
• If the top of the stack contains the operator with high or equal precedence
then pop the operators from the stack and append to the postfix expression
until the top of the stack will not contain the operator with lower
precedence.
• Let's discuss informally just memorize this point to avoid confusion, so
the inference of these two points is, that the operator which is at the upper
level of the stack must have strictly higher precedence than the one which
is at a lower level.
Algorithm
5.In the end if we have nothing to traverse in the Infix Notation
then just append all operators from the stack in the sequence of
pop operations.

Example:

• Infix:

• 8-2*7+(6/3)
Infix: 8-2*7+(6/3)
Operand 8, hence apply Rule No. 3
Infix: 8-2*7+(6/3)
2. Operator -, hence apply Rule No. 4A
Infix: 8-2*7+(6/3)
Operand 2, hence apply Rule No. 3
Infix: 8-2*7+(6/3)
Operator *, hence apply Rule No. 4A
Infix: 8-2*7+(6/3)
Operand 7, hence apply Rule No. 3
Infix: 8-2*7+(6/3)
Operator +, hence apply Rule No. 4B
Infix: 8-2*7+(6/3)
OpenParenthesisesis (, hence apply Rule No. 1
Infix: 8-2*7+(6/3)
Operand 6, hence apply Rule No. 3
Infix: 8-2*7+(6/3)
Operator /, hence apply Rule No. 4A
Infix: 8-2*7+(6/3)
Operand 3, hence apply Rule No. 3
Infix: 8-2*7+(6/3)
Closing Paranthesis ), hence apply Rule No. 2
Infix: 8-2*7+(6/3)
Nothing Left in the Infix Notation, hence apply Rule No. 5
Postfix:

827*-63/+
Input:
Output:
Evaluation of Postfix
Expressions
Operand1 Operand2 Operator
Postfix Expression Evaluation using Stack Data Structure
• A postfix expression can be evaluated using the Stack data structure.
To evaluate a postfix expression using Stack data structure we can use
the following steps...
• Read all the symbols one by one from left to right in the given
Postfix Expression
• If the reading symbol is operand, then push it on to the Stack.
• If the reading symbol is operator (+ , - , * , / etc.,), then perform
TWO pop operations and store the two popped oparands in two
different variables (operand1 and operand2). Then perform
reading symbol operation using operand1 and operand2 and push
result back on to the Stack.
• Finally! perform a pop operation and display the popped value as
final result.
Example
Example 2: Postfix Expression: 231*+9-

Steps:
1.Scan each symbol from left to right:
•If it's an operand (number), push it onto the stack.
•If it's an operator (+, -, *, /), pop two elements from the
stack, apply the operator, and push the result back onto the
stack.
Example:

231*+9-
231*+9-

Final Result:
•The final result is -4.
Postfix Expression: 82/3-4+
Example 2: Postfix Expression: 82/3-4+

Final Result:
•The final result is 5.
Postfix Expression: 53+62/*35*+
Example 3: Postfix Expression: 53+62/*35*+

Final Result:
•The final result is 39.
Example 4: Postfix Expression: 6523+8*+3+*

Final Result:
•The final result is 288.
Steps for Evaluating a Postfix Expression:

1.Scan the postfix expression from left to right.


2.If the symbol is an operand (number): Push it onto the stack.
3.If the symbol is an operator (+, -, *, /):
•Pop two operands from the stack.
•Apply the operator.
•Push the result back onto the stack.
4.At the end of the expression, the stack will contain only one element,
which is the result.
#include <stdio.h>
#include <ctype.h> // For isdigit() // Function to pop an element from the
#include <stdlib.h> // For atoi() stack
#define MAX 100 int pop() {
int stack[MAX]; if (top == -1) {
int top = -1; printf("Stack Underflow\n");
// Function to push an element onto the stack return -1;
void push(int value) { }
if (top == MAX - 1) { return stack[top--];
printf("Stack Overflow\n"); }
return;
}
stack[++top] = value;
}
// Function to evaluate a postfix expression // If the character is an operator, pop two values
from the stack, apply the operator, and push the
int evaluatePostfix(char* exp)
result back
{
else {
int i;
int val1 = pop();
// Loop through each character of the expression int val2 = pop();
for (i = 0; exp[i] != '\0'; i++) switch (exp[i]) {
{ case '+': push(val2 + val1); break;
// If the character is a digit, push it onto the stack
case '-': push(val2 - val1); break;
if (isdigit(exp[i])) {
case '*': push(val2 * val1); break;
push(exp[i] - '0'); case '/': push(val2 / val1); break;
// Convert char to int and push
}} }
} // The final result will be the only value left
in the stack
return pop();
}
int main() {
char exp[MAX];
// Input postfix expression
printf("Enter a postfix expression (e.g., 231*+9-): ");
scanf("%s", exp);
// Evaluate the postfix expression
int result = evaluatePostfix(exp);
// Print the result
printf("Result of postfix expression evaluation: %d\n", result);
return 0;
}
BALANCING SYMBOLS
BALANCING SYMBOLS
• In programming languages and mathematical
expressions, balancing symbols (like parentheses (),
square brackets [], curly braces {}, etc.) must be
correctly matched and nested to ensure the expression
is valid. A common application of stacks is to check
whether these symbols are balanced in an expression.
Balancing Symbols
• Opening symbols ((, {, [) are pushed onto the stack.
• When a closing symbol is encountered (), }, ]), we check if
the top of the stack contains the corresponding opening
symbol.
• If it matches, we pop the stack.
• If it doesn't match, or if the stack is empty when it should have an
opening symbol, the symbols are not balanced.
• The stack should be empty at the end if the symbols are
correctly balanced.
Example 1: Parentheses ()
Expression: ( (a + b) * (c + d) )

Steps:
• Push the first opening parenthesis ( onto the stack. Stack: [(]
• Push the second opening parenthesis (. Stack: [(, (]
• Encounter closing parenthesis ), pop the top of the stack (which is ().
Stack: [(]
• Push the third opening parenthesis (. Stack: [(, (]
• Encounter closing parenthesis ), pop the top of the stack (which is ().
Stack: [(]
• Encounter final closing parenthesis ), pop the stack. Stack: [] (Empty)
Example 2: Mixed Symbols (), {}, []
Expression: { [ (a + b) * (c + d) ] }
Steps:
• Push the opening curly brace {. Stack: [{]
• Push the opening square bracket [. Stack: [{, []
• Push the opening parenthesis (. Stack: [{, [, (]
• Encounter closing parenthesis ), pop the top of the stack (which is (). Stack: [{, []
• Encounter closing square bracket ], pop the top of the stack (which is [). Stack:
[{]
• Encounter closing curly brace }, pop the top of the stack (which is {). Stack: []
(Empty)
• Result: The symbols are balanced.
Tower of Hanoi
Illustrative problems - Tower of Hanoi
Procedure to solve Tower of
Hanoi
The goal of the puzzle is to
move all the disks from
leftmost peg to rightmost peg.
1.Move only one disk at a
time.
2.A larger disk may not be
p1aced on top of a smaller
disk. For example, consider
n=3 disks
Therefore n disks,
Moves-
Therefore n disks,
Moves-
Therefore n disks,
Moves-
The steps to follow are to follow recursively
• T(n-1,Beg,End,Aux)
• T(1,Beg,Aux,End), move disk from source to destination
• T(n-1,Aux,Beg,En )
Algorithm:
Main Function
Step 1: Start
Step 2: Read n
Step 3: Calculate move=pow(2,n)-1
Step 4: Function call T(n,Beg,Aux,End) recursively until n=0
Step 5: Stop

Sub Function
Step 1: If n=0, then
Step 1.1:No disk to move
Step 1.2: T(n-1,Beg,End,Aux)
T(1,Beg,Aux,End) , Move disk from source to desti ation
T(n-1,Aux,Beg,End)
#include <stdio.h>
// Function to move the disks // Step 2: Move the nth disk from source to
destination
void towerOfHanoi(int n, char source, char
destination, char auxiliary) { printf("Move disk %d from %c to %c\n", n, source,
destination);
// Base case: If there's only one disk, move
it directly
if (n == 1) { // Step 3: Move n-1 disks from auxiliary to
destination
printf("Move disk 1 from %c to %c\n",
towerOfHanoi(n - 1, auxiliary, destination, source);
source, destination);
}
return;
}
int main() {
int n = 3; // Number of disks
// Step 1: Move n-1 disks from source to
printf("Tower of Hanoi solution for %d disks:\n", n);
auxiliary
towerOfHanoi(n, 'A', 'C', 'B'); // A = Source, B =
towerOfHanoi(n - 1, source, auxiliary, Auxiliary, C = Destination
destination);
return 0;
}
Function Call
Function Call
A recursive function is defined as a function that calls itself to solve a smaller
version of its task until a final call is made which does not require a call to itself.
Since a recursive function repeatedly calls itself, it makes use of the system stack to
temporarily store the return address and local variables of the calling function.
Every recursive solution has two major cases. They are
• Base case, in which the problem is simple enough to be solved directly without
making any further calls to the same function.
• Recursive case, in which first the problem at hand is divided into simpler sub-
parts. Second the function calls itself but with sub-parts of the problem obtained
in the first step. Third, the result is obtained by combining the solutions of simpler
sub-parts.
Queue ADT
Queue ADT

• Queue is a linear data structure in which the insertion and deletion


operations are performed at two different ends.
• In a queue data structure, adding and removing elements are
performed at two different positions.
• The insertion is performed at one end and deletion is performed at
another end.
• In a queue data structure, the insertion operation is performed at a
position which is known as 'rear' and the deletion operation is
performed at a position which is known as 'front'.
• In queue data structure, the insertion and deletion operations are
performed based on FIFO (First In First Out) principle.
Introduction to Queues
• A queue is a waiting line
• It’s in daily life:
– A line of persons waiting to check out at a supermarket
– A line of persons waiting to purchase a ticket for a film
– A line of planes waiting to take off at an airport
– A line of vehicles at a toll booth
Queue ADT

In a queue data structure, the insertion operation is performed using a function called
"enQueue()" and deletion operation is performed using a function called "deQueue()".

Queue data structure can be defined as follows...


Queue data structure is a linear data structure in which the operations are
performed based on FIFO principle.

A queue data structure can also be defined as


"Queue data structure is a collection of similar data items in which insertion
and deletion operations are performed based on FIFO principle".
Example
Queue after inserting 25, 30, 51, 60 and 85.
The Queue Operations
• A queue is like a line of people waiting for a bank teller.
• The queue has a front and a rear.
The Queue Operations
• New people must enter the queue at the rear. This is
called an enqueue operation.
The Queue Operations
• When an item is taken from the queue, it always
comes from the front. This is called a dequeue
operation.
Queue Abstract Data Type
• Basic operations
– Construct a queue
– Check if empty
– Enqueue (add element to back)
– Front (retrieve value of element from front)
– Dequeue (remove element from front)
A Graphic Model of a Queue
Operations on Queues
• Insert(item): (also called enqueue)
– It adds a new item to the tail of the queue
• Remove( ): (also called delete or dequeue)
– It deletes the head item of the queue, and returns to the caller. If
the queue is already empty, this opera9on returns NULL
• getHead( ):
– Returns the value in the head element of the queue
• getTail( ):
– Returns the value in the tail element of the queue
• isEmpty( )
– Returns true if the queue has no items
• size( )
– Returns the number of items in the queue
Array representation of Queue- Insertion
Array representation of Queue- Deletion
printf("\n1.insert an element\n2.Delete an
#include<stdio.h> element\n3.Display the queue\n4.Exit\n");
#include<stdlib.h> printf("\nEnter your choice ?");
#define maxsize 5 scanf("%d",&choice);
void insert(); switch(choice) {
void delete(); case 1:
void display(); insert(); break;
int front = -1, rear = -1; case 2:
int queue[maxsize]; delete(); break;
void main () case 3:
{ display(); break;
int choice; case 4:
while(choice != 4) exit(0); break;
{ default:
void insert() { void delete() {
int item; int item;
scanf("\n%d",&item); if (front == -1 || front > rear) {
if(rear == maxsize-1) { printf("\nUNDERFLOW\n");
return;
printf("\nOVERFLOW\n");
}
return; }
else {
if(front == -1 && rear == -1) {
item = queue[front];
front = 0;
if(front == rear) {
rear = 0; } front = -1;
else { rear = -1 ;
rear = rear+1; }
} else {
queue[rear] = item; front = front + 1;
printf("\nValue inserted "); }
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]);
} } }
Linked List implementation of Queue
#include<stdio.h> void main () {
#include<stdlib.h> int choice;
struct node while(choice != 4) {
{ scanf("%d",& choice);
switch(choice) {
int data;
case 1:
struct node *next; insert(); break;
}; case 2:
struct node *front; delete(); break;
struct node *rear; case 3:
void insert(); display(); break;
case 4:
void delete();
exit(0); break;
void display();
void insert() else {
{ printf("\nEnter value?\n");
struct node *ptr; scanf("%d",&item);
int item; ptr -> data = item;
ptr = (struct node *) malloc (sizeof if(front == NULL)
(struct node)); {
if(ptr == NULL) front = ptr;
{ rear = ptr;
front -> next = NULL;
printf("\nOVERFLOW\n");
rear -> next = NULL;
return;
}
} else
{
rear -> next = ptr;
rear = ptr;
rear->next = NULL;
void delete () void display()
{ { struct node *ptr;
struct node *ptr; ptr = front;
if(front == NULL) if(front == NULL)
{ {
printf("\nUNDERFLOW\n"); printf("\nEmpty queue\n");
return; }
} else
else { printf("\nprinting values .....\n");
{ while(ptr != NULL)
ptr = front; {
front = front -> next; printf("\n%d\n",ptr -> data);
free(ptr); ptr = ptr -> next;
} } } }
}
TYPES OF QUEUES
TYPES OF QUEUES

•Simple Queue or
Linear Queue
•Circular Queue
•Priority Queue
•Double Ended
Queue (or Deque)
Simple Queue or Linear Queue
• In Linear Queue, an insertion takes place from
one end while the deletion occurs from another
end.
• The end at which the insertion takes place is
known as the rear end, and the end at which the
deletion takes place is known as front end.
• It strictly follows the FIFO rule.
Simple Queue or Linear Queue
• 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 representation of circular queue is shown in the
below image -
Circular Queue
• 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.
// Function to check if the queue is empty
#include <stdio.h> int isEmpty() {
#define SIZE 5 if (front == -1) {
// Define the size of the Circular Queue return 1;
int queue[SIZE], front = -1, rear = -1; } return 0; }
// Function to check if the queue is full // Function to insert an element in the queue
int isFull() { void enqueue(int value) {
if (isFull()) {
if ((front == 0 && rear == SIZE - 1) ||
(front == rear + 1)) { printf("Queue is Full!\n");
} else {
return 1;
if (front == -1) front = 0;
} // Initialize front
return 0; rear = (rear + 1) % SIZE;
} // Circular increment
queue[rear] = value;
printf("Inserted %d\n", value);
// Function to delete an element from the queue // Function to display the element
void dequeue() { void display() {
if (isEmpty()) { if (isEmpty()) {
printf("Queue is Empty!\n"); printf("Queue is Empty!\n");
} else { } else {
printf("Deleted %d\n", queue[front]); int i;
if (front == rear) { printf("Queue elements:\n");
// Queue has only one element, reset the queue for (i = front; i != rear;
front = rear = -1; i = (i + 1) % SIZE){
} else { printf("%d ", queue[i]);
front = (front + 1) % SIZE; }
// Circular increment printf("%d\n", queue[i]);
} } } // Display the last element
}}
int main() { case 2:
int choice, value; dequeue(); break
while (1) { case 3:
printf("\n1. Enqueue\n2. Dequeue\
display(); break;
n3. Display\n4. Exit\n");
printf("Enter your choice: "); case 4:
scanf("%d", &choice); return 0;
switch (choice) { default:
case 1: printf("Invalid cho
printf("Enter the value to insert: "); } }
scanf("%d", &value); return 0;
enqueue(value); }
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.
• The representation of priority queue is shown in the below image -
Priority Queue
• 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 -
• 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.
• 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, 3.
#include <stdio.h> // Function to check
#define SIZE 5 // if the queue is full
// Define the maximum size of the int isFull() {
//priority queue
return(rear == SIZE - 1)
struct PriorityQueue {
}
int data;
// Function to check
int priority; // if the queue is empty
}; int isEmpty() {
struct PriorityQueue pq[SIZE]; return (front == -1
// Array to store the elements and || front > rear);
their //priorities
}
int front = -1, rear = -1;
// Function to insert an element else {
in //the queue according to its int i;
priority // Shifting elements to make room for
void enqueue(int value, int //the new element in the correct position
priority) { for (i = rear; i >= front &&
pq[i].priority < priority; i--) {
if (isFull()) {
pq[i + 1] = pq[i];
printf("Queue is Full!\n");
}
return;
pq[i + 1].data = value;
} pq[i + 1].priority = priority;
if (isEmpty()) { rear++;
front = rear = 0; }
pq[rear].data = value; printf("Inserted %d with priority
%d\n", value, priority);
pq[rear].priority = priority;
// Function to remove and // Function to display the queue
//return the highest-priority //elements with their priorities
//element
void display() {
void dequeue() {
if (isEmpty()) {
if (isEmpty()) {
printf("Queue is Empty!\n");
printf("Queue is Empty!\n"); return;
return; }
} printf("Queue elements are:\n");
printf("Deleted %d with priority for (int i = front; i <= rear; i++)
%d\n", pq[front].data,
printf("Value: %d, Priority: %
pq[front].priority);
pq[i].data, pq[i].priority);
front++; }}
int main() { case 2:
int choice, value, priority; dequeue();
while (1) { break;
printf("\n1. Enqueue\n2. Dequeue\n3. case 3:
Display\n4. Exit\n"); display();
printf("Enter your choice: "); break;
scanf("%d", &choice); case 4:
switch (choice) { return 0;
case 1: default:
printf("Enter the value and its priority: "); printf("Invalid choice! Please t
scanf("%d %d", &value, &priority); again.\n");
enqueue(value, priority); } }
break; return 0;
}
Deque (or, Double Ended Queue)
• In Deque or Double Ended Queue, insertion and deletion can be done
from both ends of the queue either from the front or rear.
• It means that we can insert and delete elements from both front and
rear ends of the queue. Deque can be used as a palindrome checker
means that if we read the string from both ends, then the string would
be the same.
• Deque can be used both as stack and queue as it allows the insertion
and deletion operations on both ends. Deque can be considered as
stack because stack follows the LIFO (Last In First Out) principle in
which insertion and deletion both can be performed only from one
end. And in deque, it is possible to perform both insertion and deletion
from one end, and Deque does not follow the FIFO principle.
Deque (or, Double Ended Queue)
Two types of deque
• Input restricted deque - As the name implies, in input
restricted queue, insertion operation can be performed at only
one end, while deletion can be performed from both ends.
Two types of deque
• 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.
// Function to check if the deque is empty
#include <stdio.h> int isEmpty() {
#define SIZE 5 return (front == -1); }
// Function to insert an element at the front
// Define the size of the deque deque
int deque[SIZE]; void insertFront(int value) {
if (isFull()) {
int front = -1, rear = -1;
printf("Deque is Full!\n");
// Function to check if the deque is } else {
full if (front == -1) {
int isFull() { front = rear = 0;
} else if (front == 0) {
return (front == 0 && rear == front = SIZE - 1;
SIZE - 1) } else {
|| (front == rear + 1); front--; }
// Function to insert an element at the rear of the // Function to delete an element from the fr
deque of the deque
void insertRear(int value) { void deleteFront() {
if (isFull()) { if (isEmpty()) {
printf("Deque is Full!\n"); printf("Deque is Empty!\n");
} else { } else {
if (front == -1) { printf("Deleted %d from the front\n",
front = rear = 0; deque[front]);
} else if (rear == SIZE - 1) { if (front == rear) {
rear = 0; front = rear = -1;
// Queue becomes empty
} else {
} else if (front == SIZE - 1) {
rear++; }
front = 0;
deque[rear] = value;
} else {
printf("Inserted %d at the rear\n", value);
front++;
} }
} } }
// Function to delete an element from the rear of the // Function to display the elements of
deque void display() {
void deleteRear() { if (isEmpty()) {
if (isEmpty()) { printf("Deque is Empty!\n");
printf("Deque is Empty!\n"); } else {
} else { printf("Deque elements are: ");
printf("Deleted %d from the rear\n", int i = front;
deque[rear]);
while (i != rear) {
if (front == rear) {
printf("%d ", deque[i]);
front = rear = -1; // Queue becomes empty
i = (i + 1) % SIZE;
} else if (rear == 0) {
}
rear = SIZE - 1;
printf("%d\n", deque[i]);
} else { // Print the last element
rear--; }
} } }
int main() { case 3:
int choice, value; deleteFront();
while (1) { break;
printf("\n1. Insert Front\n2. Insert Rear\n3. Delete
Front\n4. Delete Rear\n5. Display\n6. Exit\n"); case 4:
printf("Enter your choice: "); deleteRear();
scanf("%d", &choice); break;
switch (choice) { case 5:
case 1: display();
printf("Enter the value to insert at front: ");
break;
scanf("%d", &value);
case 6:
insertFront(value);
break; return 0;
case 2: default:
printf("Enter the value to insert at rear: "); printf("Invalid choice! Please
scanf("%d", &value); n");
insertRear(value); } }
Queue ADT Applications:
Scheduling
Queue ADT Applications: Scheduling
• A Queue is a linear data structure that follows the First In,
First Out (FIFO) principle. The element inserted first will
be the first one to be removed. One of the most common and
important applications of the Queue Abstract Data Type
(ADT) is in scheduling, where tasks or processes are
managed in a sequential order.
• In scheduling systems, queues ensure that tasks are processed
in a fair, orderly manner based on their arrival or priority.
Types of Scheduling Using Queues:
1. CPU Scheduling (Process Scheduling)
• In operating systems, CPU scheduling is an essential task. The
CPU uses a queue to determine the order in which processes are
executed. It organizes processes based on their arrival time,
priority, or other factors.
Example: Round-Robin Scheduling
• In Round-Robin scheduling, each process gets an equal time slice of
the CPU. After each process finishes its time slice, it is placed at the
back of the queue, and the next process is dequeued.
Steps:
• Add processes to the queue as they arrive.
• Dequeue the process at the front, execute it for a fixed
time.
Example
Process Arrival Time Time Slice
P1 0 ms 2 ms
P2 1 ms 2 ms
P3 2 ms 2 ms

Queue Execution:
• Start with P1, execute for 2 ms.
• Move P1 to the rear if it is not finished, then move to P2.
• The CPU continues this process until all processes are completed.
2. Printer Scheduling (Job Scheduling)
• In a multi-user environment, when multiple users send print jobs to
a shared printer, the printer needs a system to schedule these jobs. A
queue is used to manage the print jobs in the order they are
received.

Example: Printer Job Queue


• Multiple users send their documents to the printer.
• The jobs are placed in a queue based on the time they arrive.
• The printer picks up the job at the front of the queue and prints it.
• Once done, it moves to the next job in the queue.
Example Scenario:
Job User Arrival Time Pages
J1 User1 10:01 5
J2 User2 10:03 3
J3 User3 10:04 7

Queue Execution:
• J1 is printed first, then J2, and finally J3.
3. Disk Scheduling

• In disk scheduling, when multiple I/O requests are made, the


disk controller manages these requests in a queue to ensure
fair and efficient access to the disk. Disk scheduling
algorithms like FCFS (First Come First Serve), SSTF
(Shortest Seek Time First), and SCAN use queue structures.
Example: FCFS Disk Scheduling
• In First Come First Serve (FCFS) scheduling, the disk
controller services requests in the order they are received.
The queue holds the sequence of requests, and the head
moves sequentially from one request to the next.
Scenario:
•Disk requests arrive at cylinder numbers:
95, 180, 34, 119, 11.
The queue manages these requests in the order:
95 → 180 → 34 → 119 → 11
4. Task Scheduling in Real-Time Systems

• In real-time systems, tasks have to be scheduled to ensure


they meet their deadlines. Queue ADTs help manage and
prioritize tasks.
Example: Real-Time Task Queue
• In an embedded system, tasks like data acquisition and
motor control are placed in a queue.
• The real-time operating system (RTOS) dequeues these tasks
and executes them within their deadlines.
Key Scheduling Algorithms Using Queues:

1. First Come, First Serve (FCFS)


• The simplest form of scheduling.
• Jobs are executed in the order they
arrive, just like elements in a queue.
Example:

Process Arrival Time Burst Time


P1 0 4
P2 1 3
P3 2 2

Consider processes P1, P2, and P3 arriving at different times.


Execution:
•P1 is processed first, followed by P2 and then P3, in the order
they arrive.
2. Round Robin (RR)
• Processes are assigned a fixed time slice
(quantum) and processed in a cyclic order
using a circular queue.
Example:
• If the time slice is 2 ms, processes will be
processed in the following sequence:
• P1 → P2 → P3 → Back to P1 (if not finished)
→ Continue until all are complete.
3. Priority Scheduling

• A priority queue is used to store processes.


• Higher-priority processes are executed
before lower-priority ones, but if two have
the same priority, they follow the FCFS
order.
Example:
Process Priority Burst Time
P1 1 4
P2 3 3
P3 2 2

Consider processes with the following priorities:


Execution:
•P2 (highest priority) is executed first, then P3, and finally P1.
#include <stdio.h>
#define SIZE 10
// Define the maximum size of the // Function to check
//queue // if the queue is full
struct Process { int isFull() {
int id;
return (rear == SIZE - 1);
int burstTime;
};
}
struct Process queue[SIZE]; // Function to check
int front = -1, rear = -1; //if the queue is empty
int timeQuantum = 2; int isEmpty() {
// Fixed time quantum for Round Robin return (front == -1 ||
//scheduling front > rear);
// Function to remove a process fro
// Function to add a process to the queue queue
void enqueue(struct Process p) { struct Process dequeue() {
if (isFull()) { struct Process p;
printf("Queue is full! Cannot add process if (isEmpty()) {
%d.\n", p.id);
printf("Queue is empty! No p
} else { dequeue.\n");
if (isEmpty()) { } else {
front = 0; p = queue[front];
} front++;
rear++; }
queue[rear] = p; return p;
printf("Process %d added to the queue with burst }
time %d.\n", p.id, p.burstTime);
// Function to display the queue elements (processes)
void displayQueue() {
if (isEmpty()) {
printf("Queue is empty!\n");
} else {
printf("Queue (Processes) [Front -> Rear]:\n");
for (int i = front; i <= rear; i++) {
printf("Process %d (Burst Time: %d)\n",
queue[i].id, queue[i].burstTime);
} } }
Process ID Burst Time
// Function to simulate Round Robin scheduling
void roundRobinScheduling() { 1 5

printf("\nRound Robin Scheduling:\n"); 2 3


while (!isEmpty()) { 3 7
struct Process currentProcess = dequeue();
Time Quantum: 2
if (currentProcess.burstTime > timeQuantum) {
Queue: [P1, P2, P
printf("Executing Process %d for %d units\n", currentProcess.id, Queue: [P2, P3
timeQuantum); Queue: [P3, P1
currentProcess.burstTime -= timeQuantum; Queue: [P1,
Queue: [P2, P3
// Reduce burst time Queue: [ P3, P1
enqueue(currentProcess);

// Requeue the process if not finished


} else {
printf("Executing Process %d for %d units (completed)\n",
currentProcess.id, currentProcess.burstTime);
int main() {
int n, burstTime;
printf("Enter the number of processes: ");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
struct Process p;
p.id = i + 1;
printf("Enter burst time for Process %d: ", p.id);
scanf("%d", &burstTime);
p.burstTime = burstTime;
enqueue(p); }
printf("\nInitial Queue:\n");
displayQueue();
THANK YOU

You might also like