Dsa Unit-3
Dsa Unit-3
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()
• 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
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:
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
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()".
•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.
Queue Execution:
• J1 is printed first, then J2, and finally J3.
3. Disk Scheduling