0% found this document useful (0 votes)
18 views89 pages

Dsaa - QB - Answer Key

The document provides algorithms for stack operations (push, pop, display) using arrays, and explains the evaluation of postfix expressions using stacks. It also discusses various applications of stacks, such as evaluating arithmetic expressions, balancing symbols, and converting infix to postfix expressions. Additionally, it introduces the concept of deques and outlines their operations.

Uploaded by

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

Dsaa - QB - Answer Key

The document provides algorithms for stack operations (push, pop, display) using arrays, and explains the evaluation of postfix expressions using stacks. It also discusses various applications of stacks, such as evaluating arithmetic expressions, balancing symbols, and converting infix to postfix expressions. Additionally, it introduces the concept of deques and outlines their operations.

Uploaded by

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

PANIMALAR ENGINEERING COLLEGE

(An Autonomous Institution, Affiliated to Anna University Chennai)


DEPARTMENT OF AIML
23AD1202 - DATA STRUCTURES AND ANALYSIS OF ALGORITHMS
PART B – ANSWER KEY
UNIT- I - ABSTRACT DATA TYPES AND LINEAR DATA STRUCTURES
11. Construct algorithm for push, pop and display operations of stack using array.
Stack is a linear data structure that follows LIFO (Last In First Out) Principle, the last element
inserted is the first to be popped out. It means both insertion and deletion operations happen at one end only.

Push Operation on Stack:


Adds an item to the stack. If the stack is full, then it is said to be an Overflow condition.
Algorithm for Push Operation:
 Before pushing the element to the stack, we check if the stack is full.
 If the stack is full (top == capacity-1), then Stack Overflows and we cannot insert the element to the
stack.
 Otherwise, we increment the value of top by 1 (top = top + 1) and the new value is inserted at top
position.
 The elements can be pushed into the stack till we reach the capacity of the stack.

Pseudo code:
if(top==(Capacity-1)):
print(“Stack Overflow”) else:
top=top+1 stack[top]=data

Pop Operation in Stack:


Removes an item from the stack. The items are popped in the reversed order in which they are pushed.
If the stack is empty, then it is said to be an Underflow condition.
Algorithm for Pop Operation:
 Before popping the element from the stack, we check if the stack is empty.
 If the stack is empty (top == -1), then Stack Underflows and we cannot remove any element from the
stack.
 Otherwise, we store the value at top, decrement the value of top by 1 (top = top – 1) and return the
stored top value.
Pseudo Code:
if(top==-1):
print(“stack Underflow”) else:
value=stack[top]
top=top-1

Display Operation on Stack:


Displays all items in the stack. If the stack is empty, then it is said to be an Underflow condition.

Algorithm for Display Operation:

 Before displaying the elements of the stack, we check if the stack is empty.
 If the stack is empty (top == -1), then Stack Underflows, and there is nothing to display.
 Otherwise, we display elements from the top of the stack down to the bottom (index 0).
 This shows the current content of the stack from top to bottom.

Pseudo Code:
if(top == -1):
print("Stack Underflow")
else:
for i in range(top, -1, -1):
print(stack[i])

12. Explain the evaluation of postfix expression algorithm and evaluate the given expression 546+*493/+*
using stack.
 Stack is the ideal data structure to evaluate the postfix expression because the top element is always the most
recent operand. The next element on the Stack is the second most recent operand to be operated on.
 Before evaluating the postfix expression, the following conditions must be checked. If any one of the
conditions fails, the postfix expression is invalid.
 When an operator encounters the scanning process, the Stack must contain a pair of operands or
intermediate results previously calculated.
 When an expression has been completely evaluated, the Stack must contain exactly one value.
Steps:
1. Initialize an empty stack to store operands.
2. Scan the postfix expression from left to right.
3. For each symbol in the expression:
o If the symbol is an operand (number), push it onto the stack.
o If the symbol is an operator (+, -, *, /):
 Pop the top two operands from the stack.
 Perform the operation (second popped element operator first popped element).
 Push the result back onto the stack.
4. After processing all symbols, the result will be the only value left in the stack.
13. Explain the stack applications with example.
1. Evaluation of Arithmetic Expressions
2. Balancing Symbols
3. Backtracking
4. Delimiter Checking
5. Reverse a Data
6. Processing Function Calls

1. Evaluation of Arithmetic Expressions


 A stack is a very effective data structure for evaluating arithmetic expressions in programming languages. An
arithmetic expression consists of operands and operators.
 In addition to operands and operators, the arithmetic expression may also include parenthesis like "left
parenthesis" and "right parenthesis".
Example: A + (B - C)
Evaluation of Arithmetic Expression requires two steps:
 First, convert the given expression into special notation.
 Evaluate the expression in this new notation.

Notations for Arithmetic Expression


There are three notations to represent an arithmetic expression:
 Infix Notation
 Prefix Notation
 Postfix Notation

Infix Notation
The infix notation is a convenient way of writing an expression in which each operator is placed between
the operands. Infix expressions can be parenthesized or unparenthesized depending upon the problem requirement.
Example: A + B, (C - D) etc.
All these expressions are in infix notation because the operator comes between the operands.
Prefix Notation
The prefix notation places the operator before the operands. This notation was introduced by the Polish
mathematician and hence often referred to as polish notation.
Example: + A B, -CD etc.
All these expressions are in prefix notation because the operator comes before the operands.
Postfix Notation
The postfix notation places the operator after the operands. This notation is just the reverse of Polish
notation and also known as Reverse Polish notation.
Example: AB +, CD+, etc.
All these expressions are in postfix notation because the operator comes after the operands.

Conversion of Arithmetic Expression into various Notations:

Infix Notation Prefix Notation Postfix Notation


A*B *AB AB*
(A+B)/C /+ ABC AB+C/
(A*B) + (D-C) +*AB - DC AB*DC-+
2. Balancing Symbols
Suppose we wish to check a text string T to see if the bracket characters it contains are well formed. We use the term brackets
here to include character p tinct opening and closing characters such as { }, < >, and ( ). The brackets are well formed if the
number of openers is the same as the number of closers and, reading the string from left to right, the number of closers never
exceeds the number of openers.
• A stack can be used to verify whether a program contains balanced braces
▪ An example of balanced braces
abc{defg{ijk}{l{mn}}op}qr
▪ An example of unbalanced braces
abc{def}}{ghij{kl}m
abc{def}{ghij{kl}m
Each time you encounter a “}”, it matches an already encountered “{”. When we reach the end of the string, you have matched
each “{”
3. Backtracking
Backtracking is another application of Stack. It is a recursive algorithm that is used for solving the
optimization problem.
4.Delimiter Checking
 The common application of Stack is delimiter checking, i.e., parsing that involves analysing a source program
syntactically.
 It is also called parenthesis checking. When the compiler translates a source program written in some
programming language such as C, C++ to a machine language, it parses the program into multiple individual
parts such as variable names, keywords, etc.
 By scanning from left to right. The main problem encountered while translating is the unmatched delimiters.
 We make use of different types of delimiters include the parenthesis checking (,), curly braces {,} and square
brackets [,], and common delimiters /* and */.
 Every opening delimiter must match a closing delimiter, i.e., every opening parenthesis should be
followed by a matching closing parenthesis.
 Also, the delimiter can be nested. The opening delimiter that occurs later in the source program should
be closed before those occurring earlier.

Valid Delimiter Invalid Delimiter

While ( i> 0) While ( i>


/* Data Structure */ /* Data Structure
{ ( a + b) - c } { ( a + b) - c
 To perform a delimiter checking, the compiler makes use of a stack.
 When a compiler translates a source program, it reads the characters one at a time, and if it finds an opening
delimiter it places it on a stack.
 When a closing delimiter is found, it pops up the opening delimiter from the top of the Stack and matches it
with the closing delimiter.
5.Reverse a Data:
 To reverse a given set of data, we need to reorder the data so that the first and last elements are exchanged, the
second and second last element are exchanged, and so on for all other elements.
Example: Suppose we have a string Welcome, then on reversing it would be Emoclew.
Reverse a String:
 A Stack can be used to reverse the characters of a string. This can be achieved by simply pushing one by one
each character onto the Stack, which later can be popped from the Stack one by one.
 Because of the last in first out property of the Stack, the first character of the Stack is on the bottom of the
Stack and the last character of the String is on the Top of the Stack and after performing the pop operation in
the Stack, the Stack returns the String in Reverse order.
6.Processing Function Calls:
Stack plays an important role in programs that call several functions in succession.

14. Write an algorithm to convert infix to postfix expression. Apply the algorithm for the given expression (A+B/C+D*(E-
F)^G)
 To convert infix expression to postfix expression, use the stack data structure. Scan the infix expression
from left to right.
 Whenever we get an operand, add it to the postfix expression and if we get an operator or parenthesis add it
to the stack by maintaining their precedence.

ALGORITHM:
 Scan the infix expression from left to right.
 If the scanned character is an operand, put it in the postfix expression.
 Otherwise, do the following
o If the precedence of the current scanned operator is higher than the precedence of the operator
on top of the stack, or if the stack is empty, or if the stack contains a ‘(‘, then push the
current operator onto the stack.
o Else, pop all operators from the stack that have precedence higher than or equal to that of the
current operator. After that push the current operator onto the stack.
 If the scanned character is a ‘(‘, push it to the stack.
 If the scanned character is a ‘)’, pop the stack and output it until a ‘(‘ is encountered, and discard both
the parenthesis.
 Repeat steps 2-5 until the infix expression is scanned.
 Once the scanning is over, Pop the stack and add the operators in the postfix expression until it is not empty.
 Finally, print the postfix expression.

Expression:
(A + B / C + D * (E - F) ^ G)

Symbol Stack Output


( (
A ( A
+ (+ A
B (+ AB
/ (+ / AB
C (+ / ABC
) + ABC/
+ + ABC/
D + ABC/D
* +* ABC/D
( +*( ABC/D
E +*( ABC/DE
- + * (- ABC/DE
F + * (- ABC/DEF
) +* ABC/DEF-
^ +*^ ABC/DEF-
G +*^ ABC/DEF-G
End + ABC/+DEF-G^*

Final Postfix Expression:

ABC/+DEF-G^*+

15. Explain the algorithm to convert infix to postfix expression with example.

 To convert infix expression to postfix expression, use the stack data structure. Scan the infix expression
from left to right.
 Whenever we get an operand, add it to the postfix expression and if we get an operator or parenthesis add it
to the stack by maintaining their precedence.

ALGORITHM:
 Scan the infix expression from left to right.
 If the scanned character is an operand, put it in the postfix expression.
 Otherwise, do the following
o If the precedence of the current scanned operator is higher than the precedence of the operator
on top of the stack, or if the stack is empty, or if the stack contains a ‘(‘, then push the
current operator onto the stack.
o Else, pop all operators from the stack that have precedence higher than or equal to that of the
current operator. After that push the current operator onto the stack.
 If the scanned character is a ‘(‘, push it to the stack.
 If the scanned character is a ‘)’, pop the stack and output it until a ‘(‘ is encountered, and discard both
the parenthesis.
 Repeat steps 2-5 until the infix expression is scanned.
 Once the scanning is over, Pop the stack and add the operators in the postfix expression until it is not
empty.
 Finally, print the postfix expression.

Example: Infix expression: K + L - M*N + (O^P) * W/U/V * T + Q

Element Stack contents Postfix Expression

K K
+ +
L + KL
- - KL+
M - KL+M
* -* KL+M
N -* KL+MN
+ + KL+MN*-
( +( KL+MN*-
O +(^ KL+MN*-O
^ +(^ KL+MN*-O
P +(^ KL+MN*-OP
) + KL+MN*-OP^
* +* K L + M N* - O P ^
W +* K L + M N* - O P ^ W
/ +/ K L + M N* - O P ^ W *
U +/ K L + M N* - O P ^W * U
/ +/ K L + M N* - O P ^W * U /
V +/ K L + M N* - O P ^ W * U / V
* +* K L + M N* - O P ^W * U / V /
T +* K L + M N* - O P ^W * U / V / T
+ + K L + M N* - O P ^W * U / V / T * +
Q + K L + M N* - O P ^W * U / V / T * + Q
K L + M N* - O P ^W * U / V / T * + Q+

16. What is meant by deque? Discuss its operations using algorithms.


 The deque stands for Double Ended Queue. Deque is a linear data structure where the insertion and
deletion operations are performed from both ends.

Operations performed on deque


There are the following operations that can be applied on a deque -
o Insertion at front
o Insertion at rear
o Deletion at front
o Deletion at rear

1. Insertion at the front end


 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.

2. Insertion at the rear end


 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, find the position of the rear pointer (rear=(rear+1)%capacity).
o 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. Deletion at the front end
 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 -
 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)%capacity).

4. Deletion at the rear end


 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.
 If rear = 0 (rear is at front), then set rear = n - 1.
 Else, decrement the rear by 1 (or, rear = rear -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)%capacity, or front = 0 and rear = n - 1 it means that the deque is full.

17. What is the importance of Balancing Symbols in expressions? Explain with an example how stacks help
in checking balanced parentheses.
 Given a string s representing an expression containing various types of brackets: {}, (), and [], the task is to
determine whether the brackets in the expression are balanced or not.
 A balanced expression is one where every opening bracket has a corresponding closing bracket in the
correct order.
 First recently opened symbol must be closed first.
ALGORITHM:
 Create an empty stack.
 Iterate through each character in the expression.
 If the character is an opening symbol (e.g., '(', '[', '{'), push it onto the stack.
 If the character is a closing symbol (')', ']', '}'):
o Check if the stack is empty. If it is, return "Unbalanced".
o Pop the top element from the stack.
o If the popped opening symbol does not match the current closing symbol, return
"Unbalanced".
 After the iteration, if the stack is not empty, return "Unbalanced".
 If the stack is empty, return "Balanced".

18. Illustrate Circular Queue ADT and its operations with suitable example.
 Circular queue in data structure provides an efficient way to use available space by wrapping around when the
end of the queue is reached.
 A circular queue is a special type of queue in data structures where the last position is connected back to
the first position, forming a circle.
 This means when the queue reaches the end, it wraps around to the beginning. This helps use all available space
efficiently without leaving any gaps.

For example, if you keep adding items to a circular queue, it will fill up from the start again after reaching the end.
This is useful for tasks like managing waiting lists or buffering data in a computer system, ensuring no space is
wasted.

Circular Queue Operations


Circular queues have several key operations that allow for the efficient management of elements:
1. Enqueue (Adding an element)
Add an element to the rear of the circular queue.
Steps:
 Check if the queue is full. If (rear + 1) % size == front, the queue is full.
 If the queue is not full, update the rear pointer to (rear + 1) % size.
 Insert the new element at the rear position.
 If the queue was initially empty (i.e., front was -1), set front to 0.
2. Dequeue (Removing an element)
Remove an element from the front of the circular queue.
Steps:
 Check if the queue is empty. If front == -1, the queue is empty.
 If the queue is not empty, remove the element at the front position.
 Update the front pointer to (front + 1) % size.
 If the queue becomes empty after the dequeue operation (i.e., front equals rear), reset both front and rear to -1.
Example:

19. Implement the Queue ADT alongwith the operations in detail.


 A queue is a useful data structure in programming. Queue follows the First In First Out (FIFO) rule - the
item that goes in first is the item that comes out first.
 A queue can be defined as an ordered list which enables insert operations to be performed at one end called
REAR and delete operations to be performed at another end called FRONT.
 In programming terms, putting items in the queue is called enqueue, and removing items from the queue is
called dequeue.

It is similar to the ticket queue outside a cinema hall, where the first person entering the queue is the first person who
gets the ticket.
Some other applications of the queue in real life are:
 People on an escalator
 Cashier line in a store
 A car wash line
 One way exits

Operations performed on queue:


The fundamental operations that can be performed on queue are listed as follows -
 Enqueue: The Enqueue operation is used to insert the element at the rear end of the queue.
 Dequeue: It performs the deletion from the front-end of the queue. It also returns the element which
has been removed from the front-end. It returns an integer value.
 Peek: This is the third operation that returns the element, which is pointed by the front pointer in the
queue but does not delete it.
 Queue overflow (isfull): It shows the overflow condition when the queue is completely full.
 Queue underflow (isempty): It shows the underflow condition when the Queue is empty, i.e., no elements
are in the Queue.

Queue Insertion Operation: Enqueue()


The enqueue() is used to insert elements into the queue. The following algorithm describes the enqueue() operation
in a simpler way.
Algorithm
1. Check if the queue is full.
2. If the queue is full, produce overflow error and exit.
3. If the queue is not full, increment rear pointer to point the next empty space.
4. Add data element to the queue location, where the rear is pointing.
Example:
Pseudo Code:
if(rear==capacity-1):
print” queue overflow” elif(front==-
1 and rear == -1):
set front and rear at 0 front=0
rear=0
else:
rear=rear+1 queue[rear]=item

DEQUEUE:
The dequeue() is a data manipulation operation that is used to remove elements from the queue. The
following algorithm describes the dequeue() operation in a simpler way.
Algorithm:
1. Check if the queue is empty.
2. If the queue is empty, produce underflow error and exit.
3. If the queue is not empty, access the data where frontis pointing.
4. Increment front pointer to point to the next available data element.
Example:

Pseudo code:
if(front == -1 and rear ==-1):
print (“queue underflow”)
elif(front==rear):
front=-1
rear=-1
else:
front=front+1
20. Evaluate the role of stacks in arithmetic expression evaluation. Show step-by-step computation
for the given expression " A * B + (C - D / E) " using a stack.
Iterate the expression from left to right and keep on storing the operands into a stack. Once an operator is received,
pop the two topmost elements and evaluate them and push the result in the stack again.
ALGORITHM:
 "Stack-based postfix evaluation" Scan the string from left to right.
 When you come across an operand, place it on the stack; when you come across an operator, remove the
corresponding operands from the stack, perform the operation, and place the result back on the stack.
 When you finish searching the expression, the value obtained is still on the stack.

Given expression:
A * B + (C - D / E)

Step 1: Convert Infix to Postfix

Symbol Stack Output


A A
* * A
B * AB
+ + AB*
( +( AB*
C +( AB*C
- + (- AB*C
D + (- AB*CD
/ + (- / AB*CD
E + (- / AB*CDE
) + AB*CDE/-
End AB*CDE/-+

Final Postfix Expression:A B * C D E / - +

Step 2: Evaluate Postfix Expression

Using Stack for Evaluation


We will evaluate A B * C D E / - + step by step.

Symbol Stack Action


A Push A
B Push B
* Pop B, A → Compute A * B → Push result
C Push C
D Push D
E Push E
/ Pop E, D → Compute D / E → Push result
- Pop result, C → Compute C - (D / E) → Push result
+ Pop result, A * B → Compute (A * B) + (C - D / E) → Push result

Final Computed Result:(A * B) + (C - D / E)


The final value in the stack is the result of the given arithmetic expression.
UNIT – II - NON-LINEAR DATA STRUCTURES – TREES

1. Define a binary tree and explain its properties. Explain tree traversal techniques with an example.
The Binary tree means that the node can have maximum two children. Here, binary name itself suggests that 'two'; therefore,
each node can have either 0, 1 or 2 children.

Properties of Binary Tree:


• The number of nodes n in a full binary tree, is at least n= 2h+1 and at most n = 2h+1-1,where h is the height of the tree.
• A binary tree of n elements has n-1 edges.
• A binary tree of height h has at least h and at most 2h - 1 elements
• A tree consisting of only a root node has a height of 0.
• The number of leaf nodes in a perfect binary tree, is l= (n+1)/2. This means that a perfect binary tree with l leaves has n=2l-1 nodes.
• The number of internal nodes in a complete binary tree of n nodes is n/2.

BINARY TREE TRAVERSALS


There are three standard ways of traversing a binary tree T with root R. They are:
( i ) Preorder Traversal
(ii ) In order Traversal
(iii) Post order Traversal
General outline of these three traversal methods can be given as follows:

Preorder Traversal:
(1) Process the root R.
(2) Traverse the left subtree of R in preorder.
(3) Traverse the right subtree of R in preorder.
In the preorder traversal the node element is visited first and then the right subtree of the node and then the right subtree of the node is
visited. Consider the following case where we have 6nodes in the tree A, B, C, D, E, F.
Algorithm - Preorder Traversal
PREORDER( ROOT )
Temp = ROOT
If temp = NULL
Return
End if
Print info(temp)
If left(temp) ≠ NULL
PREORDER( left(temp))
End if
If right(temp) ≠ NULL
PREORDER(right(temp))
End if
End PREORDER

In order Traversal:
(1) Traverse the left subtree of R in in order.
(2) Process the root R.
(3) Traverse the right subtree of R in in order.
In the In order traversal method, the left subtree of the current node is visited first and then the current node is processed and at last the
right subtree of the current node is visited.
Algorithm - Inorder Traversal
INORDER( ROOT )
Temp = ROOT
If temp = NULL
Return
End if
If left(temp) ≠ NULL
INORDER(left(temp))
End if
Print info(temp)
If right(temp) ≠ NULL
INORDER(right(temp))
End if
End INORDER
Post order Traversal:
(1) Traverse the left subtree of R in post order.
(2) Traverse the right subtree of R in post order.
(3) Process the root R.
In the post order traversal method, the left subtree is visited first, then the right subtree and at last the current node is processed.
Algorithm - Postorder Traversal
POSTORDER( ROOT )
Temp = ROOT
If temp=NULL
Return
End if
If left(temp) ≠NULL
POSTORDER(left(temp))
End if
If right (temp) ≠NULL
POSTORDER(right(temp))
End if
Print info(temp)
End POSTORDER

12. Illustrate insert, delete, and search operations of BST.


A binary search tree follows some order to arrange the elements. In a Binary search tree, the value of left node
must be smaller than the parent node, and the value of right node must be greater than the parent node. This rule is
applied recursively to the left and right subtrees of the root.

Operations of BST.
S. No Operation Description
1 Searching in BST Finding the location of some specific element in a binary search
tree
2 Insertion in BST Adding a new element to the binary search tree at the appropriate
location so that the property of BST do not violate.
3 Deletion in BST Deleting some specific node from a binary search tree.
However, there can be various cases in deletion depending upon the
number of children, the node have.

1. INSERTION OPERATION IN BST:


 First, we have to insert the first element into the tree as the root of the tree.
 Then, read the next element; if it is smaller than the root node, insert it as the root of the left subtree, and
move to the next element.
 Otherwise, if the element is larger than the root node, then insert it as the root of the right subtree.

2. DELETION OPERATION IN BST:


Deletion operation is the complex operation in the Binary search tree. To delete an element, consider the following three
possibilities.
CASE 1: Node with no children.
CASE 2: Node with one child.
CASE 3: Node with two children.
CASE: 1 [Node with no children]
If the node is a leaf node, it can be deleted immediately.

CASE: 2 [Node with one child]


If the node has one child, it can be deleted by adjusting its parent pointer that points to its child node.
Case: 3 [Node with two children]
 It is difficult to delete a node which has two children.
 The general strategy is to replace the data of the node to be deleted with its smallest data of the right subtree or largest
data of the left subtree.

3. SEARCHING OPERATION IN BST:

 Searching means finding or locating some specific element or node within a data structure. However, searching for some
specific node in binary search tree is pretty easy due to the fact that, element in BST is stored in a particular order.
 Compare the element with the root of the tree.
 If the item is matched then return the location of the node.
 Otherwise check if item is less than the element present on root, if so then move to the left sub-tree.
 If not, then move to the right sub-tree.
 Repeat this procedure recursively until match found.
 If element is not found then return NULL.

13. Write an algorithm for searching in a Splay Tree and demonstrate its working with an example.
Splay Tree in Data Structure is a self-balancing (or self-adjusting) Binary Search Tree, and these are
the roughly-balanced (not strictly balanced) Binary Search Trees. All the operations in Splay Tree, such as
Searching, Insertion, and Deletion, are the same as in the Binary Search Tree.
But every operation is followed by one extra operation called splaying, which is used to balance (or adjust)
the given binary search tree.
Splaying is an operation that places a particular node to a root node by rearrangements or rotations in the
tree. So, the rotations will be performed on such particular nodes.
Splay trees are the altered versions of the Binary Search Trees, since it contains all the operations of BSTs,
like insertion, deletion and searching, followed by another extended operation called splaying.
There are six types of rotations for it.
 Zig rotation
 Zag rotation
 Zig-Zig rotation
 Zag-Zag rotation
 Zig-Zag rotation
 Zag-Zig rotation
Algorithm for Searching in a Splay Tree

1. Start from the root node.


2. Traverse the tree like a normal BST search:
o If the key matches the current node, move to step 3.
o If the key is smaller, move left.
o If the key is larger, move right.
o If a null pointer is reached, return NULL (element not found).
3. Splay the found node to the root using rotations:
o Zig Rotation (Single rotation) – when the node is a left or right child of the root.
o Zig-Zig Rotation (Double rotation) – when the node is a left-left or right-right grandchild.
o Zig-Zag Rotation (Double rotation) – when the node is a left-right or right-left grandchild.
4. Return the found node (now at the root).

Example:
14. Define AVL Trees and explain how they maintain balance.

AVL Tree can be defined as height balanced binary search tree in which each node is associated with a
balance factor which is calculated by subtracting the height of its right sub-tree from that of its left sub tree.Tree is
said to be balanced if balance factor of each node is in between -1 to 1, otherwise, the tree will be unbalanced and
need to be balanced.

AVL Rotations
We perform rotation in AVL tree only in case if Balance Factor is other than -1, 0, and 1.
There are basically four types of rotations which are as follows:
1. L L rotation: Inserted node is in the left subtree of left subtree of A
2. R Rrotation : Inserted node is in the right subtree of right subtree of A
3. L R rotation : Inserted node is in the right subtree of left subtree of A
4. R L rotation : Inserted node is in the left subtree of right subtree of A Where node A is the node whose
balance Factor is other than -1, 0, 1. BF(N) = height (left subtree) − height (right subtree)

Properties:
The balancing factor can be -1, 0, or 1 for a balanced AVL tree.
 BF = -1: The right subtree is one level higher than the left subtree.
 BF = 0: The left and right subtrees are of equal height.
 BF = 1: The left subtree is one level higher than the right subtree.

1. LL Rotation
2. RR Rotation

3. LR Rotation

Double rotations are bit tougher than single rotation which has already explained above. LR rotation = RR
rotation + LL rotation, i.e., first RR rotation is performed on subtree and then LL rotation is performed on full tree,
by full tree we mean the first node from the path of inserted node whose balance factor is other than -1, 0, or 1.

4. RL Rotation
As already discussed, that double rotations are bit tougher than single rotation which has already explained above. R L
rotation = LL rotation + RR rotation, i.e., first LL rotation is performed on subtree and then RR rotation is performed on
full tree, by full tree we mean the first node from the path of inserted node whose balance factor is other than -1, 0, or 1.

15. Construct a BST by inserting 3,1,4,9,6,5,2,8 and 7 elements. Show the results of deleting the nodes 1, 6
and 7 one after other of the constructed tree.
A binary search tree follows some order to arrange the elements. In a Binary search tree, the value of left node must
be smaller than the parent node, and the value of right node must be greater than the parent node. This rule is applied
recursively to the left and right subtrees of the root.
INSERTION:
 First, we have to insert the first element into the tree as the root of the tree.
 Then, read the next element; if it is smaller than the root node, insert it as the root of the left subtree, and
move to the next element.
 Otherwise, if the element is larger than the root node, then insert it as the root of the right subtree.
Deleting the nodes 1, 6 and 7 one after other
16. What are Red-Black Trees? Explain their properties and balancing rules.
Red-Black Trees are another type of the Balanced Binary Search Trees with two coloured nodes: Red and
Black. It is a self-balancing binary search tree that makes use of these colours to maintain the balance factor during
the insertion and deletion operations. Hence, during the Red Black Tree operations, the memory uses 1 bit of
storage to accommodate the colour information of each node

Properties of a Red-Black Tree

The Red-Black tree satisfies all the properties of binary search tree in addition to that it satisfies following additional properties –
1. Root property: The root is black.
2. External property: Every leaf (Leaf is a NULL child of a node) is black in Red-Black tree.
3. Internal property: The children of a red node are black. Hence possible parent of red node is a black node.
4. Depth property: All the leaves have the same black depth.
5. Path property: Every simple path from root to descendant leaf node contains same number of black nodes.
The result of all these above-mentioned properties is that the Red-Black tree is roughly balanced.

Rules That Every Red-Black Tree Follows:


1. Every node has a color either red or black.
2. The root of the tree is always black.
3. There are no two adjacent red nodes (A red node cannot have a red parent or red child).
4. Every path from a node (including root) to any of its descendants NULL nodes has the same number of black nodes.
5. Every leaf (e.i. NULL node) must be colored BLACK

Operations of Red-Black Trees:


The operations on Red-Black Trees include all the basic operations usually performed on a Binary Search Tree. Some of the basic
operations of an RB Tree include:
 Insertion
 Deletion
 Search

Balancing Rules (Rebalancing After Insertion & Deletion)


When inserting or deleting nodes, the Red-Black Tree might violate its properties, so rotations and recoloring are used to
restore balance.
Rotations
 Left Rotation: Moves a node up and its right child down.
 Right Rotation: Moves a node up and its left child down.

Fixing Violations After Insertion


If a Red-Black Tree rule is violated after insertion:
1. Recoloring: If the parent and uncle are Red, they are recolored Black, and the grandparent
becomes Red.
2. Rotations: If recoloring doesn’t fix the issue, perform a left or right rotation.
Fixing Violations After Deletion
 Deleting a node may cause a black height violation:
1. If the removed node was Red, no issue arises.
2. If it was Black, balance is restored using recoloring and rotations.
17. Illustrate with an example how insertion and deletion operations are performed in an AVL tree.
AVL Tree can be defined as height balanced binary search tree in which each node is associated with a
balance factor which is calculated by subtracting the height of its right sub-tree from that of its left sub tree. Tree is
said to be balanced if balance factor of each node is in between -1 to 1, otherwise, the tree will be unbalanced and
need to be balanced.

AVL Rotations
We perform rotation in AVL tree only in case if Balance Factor is other than -1, 0, and 1.
There are basically four types of rotations which are as follows:
1. L L rotation: Inserted node is in the left subtree of left subtree of A
2. R R rotation : Inserted node is in the right subtree of right subtree of A
3. L R rotation : Inserted node is in the right subtree of left subtree of A
4. R L rotation : Inserted node is in the left subtree of right subtree of A Where node A is
the node whose balance Factor is other than -1, 0, 1. BF(N) = height (left subtree)
− height (right subtree)
Steps for Insertion in an AVL Tree
1. Perform Standard BST Insertion
 Insert the new node just like in a Binary Search Tree (BST).
2. Update Balance Factor
 Calculate the Balance Factor (BF) of each node.
 If the BF is between -1 and 1, the tree remains balanced.
3. Perform Rotations If Needed
If BF becomes greater than 1 or less than -1, perform rotations to balance the tree.
Steps for Deleting a Node in an AVL Tree
1. Perform Standard BST Deletion
 Find the node to delete.
 Three possible cases:
1. Node has no children (Leaf Node) → Simply remove it.
2. Node has one child → Remove it and link the child to its parent.
3. Node has two children → Replace it with its inorder successor (smallest node in right subtree) and
delete the successor.
2. Update Heights
 After deletion, update the height of affected nodes.
3. Check for Imbalance
 Calculate the Balance Factor (BF) = Height(Left) - Height(Right).
 If BF > 1 or BF < -1, perform rotations to restore balance.
4. Perform Rotations if Needed Four cases can occur:
1. Left-Left (LL Rotation)
o BF > 1 and left subtree is heavy.
o Solution: Perform a Right Rotation.
2. Right-Right (RR Rotation)
o BF < -1 and right subtree is heavy.
o Solution: Perform a Left Rotation.
3. Left-Right (LR Rotation)
o BF > 1 and left subtree is right-heavy.
o Solution: Perform a Left Rotation on left child, then a Right Rotation.
4. Right-Left (RL Rotation)
o BF < -1 and right subtree is left-heavy.
o Solution: Perform a Right Rotation on right child, then a Left Rotation.
18. Trace the steps to construct AVL tree using the elements: 10,15,9,12,13,79,45,36,22.
AVL Tree can be defined as height balanced binary search tree in which each node is associated with a
balance factor which is calculated by subtracting the height of its right sub-tree from that of its left sub tree. Tree is
said to be balanced if balance factor of each node is in between -1 to 1, otherwise, the tree will be unbalanced and
need to be balanced.

Steps for Insertion in an AVL Tree


1. Perform Standard BST Insertion
 Insert the new node just like in a Binary Search Tree (BST).
2. Update Balance Factor
 Calculate the Balance Factor (BF) of each node.
 If the BF is between -1 and 1, the tree remains balanced.
3. Perform Rotations If Needed
If BF becomes greater than 1 or less than -1, perform rotations to balance the tree. Rotations Used in AVL Trees
Left Rotation (LL Rotation)
o Used when a node is inserted to the right of the right subtree (Right-heavy).
o Example: Inserting 30 into 10 → 20.
Right Rotation (RR Rotation)
o Used when a node is inserted to the left of the left subtree (Left-heavy).
o Example: Inserting 5 into 30 → 20.
Left-Right Rotation (LR Rotation)
o Used when a node is inserted to the right of the left subtree.
o Example: Inserting 15 into 10 → 20, where 15 is inserted into 10's right subtree.
Right-Left Rotation (RL Rotation)
o Used when a node is inserted to the left of the right subtree.
o Example: Inserting 25 into 10 → 30, where 25 is inserted into 30's left subtree.
Example
19. Implement the insertion and splaying operation in a Splay Tree for the given sequence: [10, 20, 30, 25, 40, 35]
and analyze the effect of splaying.
Splay Tree in Data Structure is a self-balancing (or self-adjusting) Binary Search Tree, and these are the
roughly-balanced (not strictly balanced) Binary Search Trees. All the operations in Splay Tree, such as Searching,
Insertion, and Deletion, are the same as in the Binary Search Tree.
But every operation is followed by one extra operation called splaying, which is used to balance (or adjust)
the given binary search tree.
Splaying is an operation that places a particular node to a root node by rearrangements orrotations in the
tree. So, the rotations will be performed on such particular nodes.
20) i) write a routine for post order traversal. Is it possible to find minimum and maximum value in binary
search tree using traversals? Discuss (ii) Display the given tree using inorder, preorder and postorder
traversals.

(iii)Delete nodes 11 and 10 from above binary tree and display tree after deletion

(i) Postorder Traversal


POSTORDER( ROOT )
Temp = ROOT
If temp=NULL
Return
End if
If left(temp) ≠NULL
POSTORDER(left(temp))
End if
If right (temp) ≠NULL
POSTORDER(right(temp))
End if
Print info(temp)
End POSTORDER

Finding Minimum and Maximum in a BST using Traversals


 Using Inorder Traversal: Since Inorder traversal (Left → Root → Right) gives a sorted sequence, the first
element is the minimum and the last element is the maximum.

(ii)Display the given tree using inorder, preorder and postorder traversals.
INORDER
Left – Root—Right
1 2 3 7 10 11 12 28 30 40
definorder_traversal(root): if
root: inorder_traversal(root.left)
print(root.key, end=" ")
inorder_traversal(root.right)

PREORDER
Root – Left – Right
7 2 1 3 12 11 10 30 28 40
defpreorder_traversal(root): if
root:
print(root.key, end=" ")
preorder_traversal(root.left)
preorder_traversal(root.right)

POSTORDER
Left → Right → Root order.
1 3 2 10 11 28 40 30 12 7
defpostorder_traversal(root):
if root:
postorder_traversal(root.left)
postorder_traversal(root.right)
print(root.key, end=" ")

(iii)Delete nodes 11 and 10 from above binary tree and display tree after deletion
Deleting Nodes 11 and 10 from the Given Tree
We will follow the BST Deletion Algorithm:
1. Find the node to delete.
2. Three Cases:
o Leaf Node (No Children) → Remove it directly.
o One Child → Replace it with its child.
o Two Children → Replace it with inorder successor (smallest node in the right subtree), then
delete the successor.
UNIT – III - DIVIDE AND CONQUER STRATEGY AND GREEDYSTRATEGY
11. Explain the Divide and Conquer approach with an example.
A divide and conquer algorithm is a method used in computer science to solve big problems by
breaking them down into smaller, more manageable parts. The idea is to "divide" the problem into smaller
pieces, "conquer" each piece by solving it, and then "combine" the solutions to get the final answer.

Divide and Conquer Working:


The steps in a divide and conquer method are straight forward and can be broken down into three main
parts: Divide, Conquer, and Combine.
1. Divide
The first step is to break down the larger problem into smaller, more manageable subproblems. This
"dividing" process continues until the subproblems are simple enough to be solved directly, often when
they reach a base case, like a single element or a very small dataset.
2. Conquer
After dividing the problem, the next step is to "conquer" each of the smaller subproblems. This usually
involves solving each subproblem individually. Depending on the algorithm, this step might involve
recursion, where the subproblems are solved using the same divide and conquer approach, or it might be
done through simple, direct computation.
3. Combine
Finally, once all the subproblems have been solved, the last step is to "combine" the solutions of these
subproblems to form the solution to the original, larger problem. This step merges the solutions in a way
that completes the task, whether it’s sorting a list, multiplying matrices, or finding the maximum value in
a dataset.

Divide and Conquer Algorithm Examples


Merge Sort is a Divide and Conquer algorithm. It divides input array in two halves, calls itself
for the two halves and then merges the two sorted halves. The merge() function is used for merging two
halves.
Step 1: start
Step 2:declare array and left, right, midvariable
Step 3: perform merge function.
ifleft>right return
mid= (left+right)/2 mergesort(array,
left, mid) mergesort(array,mid+1,right)
merge(array, left, mid, right)
Step 4: Stop
12.Sort the following array using Quick Sort and show all steps: [10, 80, 30, 90, 40, 50, 70]
Quick sort is a fast sorting algorithm used to sort a list of elements. The quick sort algorithm attempts to
separate the list of elements into two parts and then sort each part recursively. That means it use divide and
conquer strategy.
In quick sort, the partition of the list is performed based on the element called pivot. Here pivot element
is one of the elements in the list. The list is divided into two partitions such that "all elements to the left of pivot
are smaller than the pivot and all elements to the right of pivot are greater than or equal to the pivot".

Algorithm:
Step(1): Let take the first element as the ‘pivot’ element.
Step(2): Set low index of an array to “i”. Set high index of an array to “j”.
Step(3): if(A[i]<pivot)=>then increment “i”
Step(4): if(A[j]>pivot)=>then decrement “j”.
Step(5): if ‘i” cant increment and ‘j” cant decrement, then swap(A[i], A[j])
Step(6): if ‘i’ and ‘j’ are crossed (or) if (A[j]<pivot)=>then swap(A[j], pivot)

Example: Consider the list of elements – 10, 80, 30, 90, 40, 50, 70.

Step: 1
i j
10 80 30 90 40 50 70
Pivot
Step: 2 A[i]>pivot, so stop incrementing ‘i’
i j
10 80 30 90 40 50 70
Pivot
Step: 3 A[j]<pivot, so decrement ‘j’
i j
10 80 30 90 40 50 70 Pivot
Step: 4 A[j]<pivot, so decrement ‘j’
i j
10 80 30 90 40 50 70
Pivot
Step: 5 A[j]<pivot, so decrement ‘j’
i j
10 80 30 90 40 50 70
Pivot
Step: 6 A[j]<pivot, so decrement ‘j’
i j
10 80 30 90 40 50 70
Pivot
Step: 7 A[j]<pivot, so decrement ‘j’
ij
10 80 30 90 40 50 70
Pivot
Step: 8 A[j]<pivot, so decrement ‘j’
j i
10 80 30 90 40 50 70
Pivot
Step: 9 A[j]<pivot, so stop incrementing ‘j’
j i
10 80 30 90 40 50 70
Pivot
Step: 10 Swap A[j] and pivot - Both are in same position so no change
j i
10 80 30 90 40 50 70
Pivot
This is the partition 1. Now change the pivot value to 80 and do the same steps.
Step: 11
i j
10 80 30 90 40 50 70
Pivot
Step: 12 A[i]>pivot, so incrementing ‘i’
i j
10 80 30 90 40 50 70
Pivot
Step: 13 A[i]>pivot, so stop incrementing ‘i’
i j
10 80 30 90 40 50 70
Pivot
Step: 14 A[j]<pivot, so decrement ‘j’
i j
10 80 30 90 40 50 70
Pivot

Step: 15 Swap A[j] and A[i]


i j
10 80 30 70 40 50 90
Pivot
Continue the same the final sorted list will be
10 30 40 50 70 80 90

13.Discuss the process of multiplication of large integers.


Multiplication of large Integers:
There are two ways to perform large integer multiplication using divide and conquer.
1. Dumb method – does not improve the running time.
2. Clever approach – performs better than the traditional approach for integer multiplication.

Dumb Approach:

Let us represent number D as D = dn-1dn-2. . . d2 d1 d0. Each digit di is the ith least significant digit in number D.
According to position value,
45 = 4*101 + 5*100
23 = 2*101 + 3*100
For any real values a, b, c and d,
(a + b)*(c + d) = (a*c + a*d + b*c + b*d)
So, 45 * 23 = (4*101 + 5*100) * (2*101 + 3*100)
= (4 *2) *102 + (4*3 + 5*2) *101 + (5*3) *100
= 800 + 220 + 15
= 1035
Let’s derive formula to multiply two numbers of two digits each.
Consider C = A * B. If we consider A = a1a0 and B = b1b0, then
C=a1b1102+ a1 b0101+ a0 b1101+ a0 b0100
C= a1b1102+ (a1 b0+ a0 b1)101+ a0 b0100
C = A * B = c2102 + c1101 + c0100
Where,
c2 = a1 * b1
c1 = a1* b0 + a0* b1
c0 = a0 * b0
Generalization:
If size of integer is n digit, then we can generalize multiplication as,
C = c210n + c110n/2 + c0

Example: Multiply 2345 with 678 using divide and conquer approach.
Solution:
Size of both operands must be even, so pad zero to the multiplier.
A = 2345 and B = 0678
A = a1a0 = 2345, hence a1 = 23 and a0 = 45
B = b1b0 = 0678, hence b1 = 06 and b0 = 78
C = A * B =c2104 + c1102 + c0100
c2 = a1 * b1
= 23 * 06
= 138
c1 = a1*b0 + a0*b1
= (23*78) + (45 * 06)
= 2064
c0 = a0 * b0
=(45 * 78)
= 3510
C = c2102 + c1101 + c0100
= 138*104 + 2064*102 + 3510
= 15, 89, 910
Clever Approach:

Previous DC approach does four multiplications. Let’s reformulate it to reduce numbers of multiplications to three.
First approach:
According to dumb approach,
c2 = a1 * b1
c1 = a1*b0 + a0*b1 …(1)
c0 = a0*b0
Second approach:
Let us rewrite c1 as,
c1 = (a1 + a0) * (b1 + b0) – (c2 + c0)
= (a1b1 + a1b0 + a0b1 + a0b0) – (a1b1 + a0b0)
= a1*b0 + a0*b1 …(2)
Equation (1) and Equation (2) are the same, but the second approach of computing c1 involves only one multiplication rather
than two as it requires in the first approach.
For A = a1a0 = 2345,
hence, a1 = 23 and a0 = 45 and
B = b1b0 = 0678,
hence, b1 = 06 and b0 = 78
c2 = a1 * b1
= 23 * 06
= 138
c0 = a0*b0
= (45 * 78)
= 3510
c1 = (a1 + a0) * (b1 + b0) – (c2 + c0)
= (23 + 45) * (06 + 78) – (138 + 3510)
= 68 * 84 – 3648
= 2064
It is same as c1 = (a1*b0 + a0*b1) of dumb multiplication approach, but does only one multiplication rather than two.
C = c2 * 104 + c1 * 102 + c0
= 1380000 + 206400 + 3510 = 15, 89, 910
This formulation leads to same result, with three multiplications only.

14. Describe Strassen’s Matrix Multiplication Algorithm.


Strassen suggested a divide and conquer strategy-based matrix multiplication technique that requires fewer
multiplications than the traditional method. The multiplication operation is defined as follows using Strassen’s method:

C11 = P + S – T + V
C12 = R + T
C21 = Q + S
C22 = P + R – Q + U
Where,
P = (A11 + A22) * (B11 + B22)
Q = (A21 + A22) * B11
R = A11 * (B12 – B22)
S = A22 * (B21 – B11)
T = (A11 + A12) * B22
U = (A21 – A11) * (B11 + B12)
V = (A12 – A22) * (B21 + B22)

Example: Multiply the below two matrix using divide and conquer approach:
A11= 1 A12 =3 A21 =5 A22 =7
B11 =8 B12 = 4 B21 =6 B22 =2
P = (A11 + A22) * (B11 + B22)
P=(1+7)*(8+2)
P=80
Q = (A21 + A22) * B11
Q=(5+7)*8
Q=96
R = A11 * (B12 – B22)
R=1*(4-2)
R=2
S = A22 * (B21 – B11)
S=7*(6-8)
S=-14
T = (A11 + A12) * B22
T=(1+3)*2
T=8
U = (A21 – A11) * (B11 + B12)
U=(5-1)*(8+4)
U=48
V = (A12 – A22) * (B21 + B22)
V=(3-7)*(6+2)
V=-32
C11 = P + S – T + V
C11 =80-14-8-32
C11 =26
C12 = R + T
C12 =2+8
C12 =10
C21 = Q + S
C21 =96-14
C21 =82
C22 = P + R – Q + U
C22 =80+2-96+48
C22 =34
Normal Matrix Multiplication:

15. Illustrate the steps in divide-and-conquer paradigm and explain how it is applied in merge sort
algorithm. Apply the algorithm to sort the following elements: 47, 55, 22, 11, 36, 08, 77, 7

Divide and Conquer strategy:


Divide:
❖ Divide the array into two sub arrays by picking any key value in the array called pivot element.
❖ Each element in the left sub array is less than or equal to pivot element. Each element in the right sub-tree is
greater than the pivot element

Conquer:
❖ Recursively divide the sub-arrays until the array will contain only one element.

Combine:
❖ Combine all the sorted elements in a group to form a list of sorted elements.
MERGE SORT ALGORITHM
Merge Sort is a Divide and Conquer algorithm. It divides input array in two halves, calls itself for the
two halves and then merges the two sorted halves. The merge() function is used for merging two halves.
Algorithm:
Step 1: start
Step 2: declare array and left, right, midvariable
Step 3: perform merge function.
ifleft>right return
mid= (left+right)/2 mergesort(array, left, mid)
mergesort(array,mid+1,right) merge(array, left,
mid, right)
Step 4: Stop

Apply the algorithm to sort the following elements: 47, 55, 22, 11, 36, 08, 77, 7
0 1 2 3 4 5 6 7
47 55 22 11 36 08 77 7 (0+7)/2 = 3.5 = 3

0 1 2 3 0 1 2 3
(0+3)/2 = 1.5 = 1 47 55 22 11
36 08 77 7 (0+3)/2= 1.5 =1

47 55 22 11 36 08 77 7

47 55 22 11 36 08 77 7

47 55 11 22 08 36 7 77

11 22 47 55 7 08 36 77

7 08 11 22 36 47 55 77

16. Illustrate the working of Prim’s Algorithm for Minimum Spanning Tree (MST) with an example.
Prim’s Algorithm:
Prim's Algorithm is a greedy algorithm that is used to find the minimum spanning tree from a graph. Prim's algorithm finds the
subset of edges that includes every vertex of the graph such that the sum of the weights of the edges can be minimized.
Prim's algorithm starts with the single node and explores all the adjacent nodes with all the connecting edges at every step. The
edges with the minimal weights causing no cycles in the graph got selected.

Algorithm:
Step 1: Select a starting vertex
Step 2: Repeat Steps 3 and 4 until there are fringe vertices
Step 3: Select an edge 'e' connecting the tree vertex and fringe vertex that has minimum weight
Step 4: Add the selected edge and the vertex to the minimum spanning tree T [END OF LOOP]
Step 5: EXIT

Example:
Step 1: Choose any arbitrary node as root node. We choose S node as the root node of Prim's spanning tree. This
node is arbitrarily chosen, so any node can be the root node.
Step 2: Check outgoing edges and select the one with less cost

17. Apply Dijkstra’s Algorithm to find the shortest path from source vertex ‘A’ in the given graph:
A ---3--- B
| |
6| |2
| |
C ---4--- D

Dijkstra’s Algorithm-
• Dijkstra Algorithm is a very famous greedy algorithm.
• It is used for solving the single source shortest path problem.
• It computes the shortest path from one particular source node to all other remaining nodes of the graph.

Step by step process of algorithm implementation:


1. The very first step is to mark all nodes as unvisited,
2. Mark the picked starting node with a current distance of 0 and the rest nodes with infinity,
3. Now, fix the starting node as the current node,
4. For the current node, analyse all of its unvisited neighbours and measure their distances by adding the current distance of the
current node to the weight of the edge that connects the neighbour node and current node,
5. Compare the recently measured distance with the current distance assigned to the neighbouring node and make it as the new
current distance of the neighbouring node,
6. After that, consider all of the unvisited neighbours of the current node, mark the current node as visited,
7. If the destination node has been marked visited then stop, an algorithm has ended, and
8. Else, choose the unvisited node that is marked with the least distance, fix it as the new current node, and repeat the process again
from step 4.

Example:
18. Find the Minimum Spanning Tree (MST) using Kruskal’s Algorithm for the given graph:
Vertices: A, B, C, D, E
Edges: (A-B: 2), (A-C: 3), (B-C: 1), (B-D: 4), (C-D: 5), (C-E: 6), (D-E: 7)

Kruskal’s Algorithm:
Kruskal's Algorithm is used to find the minimum spanning tree for a connected weighted graph. The main target of the algorithm is
to find the subset of edges by using which we can traverse every vertex of the graph. It follows the greedy approach that finds an
optimum solution at every stage instead of focusing on a global optimum.

Algorithm steps:
Step 1: Sort all edges in increasing order of their edge weights.
Step 2: Pick the smallest edge.
Step 3: Check if the new edge creates a cycle or loop in a spanning tree.
Step 4: If it doesn’t form the cycle, then include that edge in MST. Otherwise, discard it.
Step 5: Repeat from step 2 until it includes |V| - 1 edges in MST.

19. What is Huffman Coding? Explain its steps with an example.


Huffman Coding:
Huffman coding is a lossless data compression algorithm. Huffman Coding is a technique of compressing data to reduce its size
without losing any of the details. It was first developed by David Huffman.

Huffman coding is done with the help of the following steps.


1. Calculate the frequency of each character in the string.
2. Sort the characters in increasing order of the frequency.
3. Make each unique character as a leaf node.
4. Create an empty node z. Assign the minimum frequency to the left child of z and assign the second minimum frequency to the
right child of z. Set the value of the z as the sum of the above two minimum frequencies.
5. Remove these two minimum frequencies and add the sum into the list of frequencies
6. Insert node z into the tree.
7. Repeat steps 3 to 5 for all the characters
8. For each non-leaf node, assign 0 to the left edge and 1 to the right edge.

Example:

20.Discuss prim’s algorithm to find minimum spanning tree and analyze its complexity. Apply the algorithm to find the
minimum spanning tree for the following graph.
Prim’s Algorithm:
Prim's Algorithm is a greedy algorithm that is used to find the minimum spanning tree from a graph. Prim's algorithm finds the
subset of edges that includes every vertex of the graph such that the sum of the weights of the edges can be minimized.
Prim's algorithm starts with the single node and explores all the adjacent nodes with all the connecting edges at every step. The
edges with the minimal weights causing no cycles in the graph got selected.

Algorithm:
Step 1: Select a starting vertex
Step 2: Repeat Steps 3 and 4 until there are fringe vertices
Step 3: Select an edge 'e' connecting the tree vertex and fringe vertex that has minimum weight
Step 4: Add the selected edge and the vertex to the minimum spanning tree T
Step 5: EXIT

Example:

UNIT- IV - DYNAMIC PROGRAMMING AND BACKTRACKING


11. Apply dynamic programming to compute the Binomial Coefficient and solve C(6,4) step by step.
Dynamic programming is a technique for solving a complex problem by first breaking into a collection of simpler
subproblems, solving each subproblem just once, and then storing their solutions to avoid repetitive computation.
The main use of dynamic programming is to solve optimization problems.
Computing Binomial Coefficient:
Binomial coefficient is a coefficient of a term in the expansion of the binomial(x+y)n .
According to the binomial theorem, (x+y)n = nC0 xn y0 +nC1 xn-1y1+nC2 xn-2 y2 +………+nCn x0yn Where nC0+nC1
+nC2 +………+nCn are binomial coefficient.
Binomial Coefficient using Dynamic Programming:

Example: Illustrate how binomial coefficient is found using dynamic programming and apply the same to
find C(6,4).
Solution:

Formula:

r→ n 0 1 2 3 4

0 1
1 1 1
2 1 1
3 1 1
4 1 1
5 1
6 1

C[i,j] = C[i-1], + C[i-1, j]

By the above formula:

C[2,1] = C[1,1]+C[1,0] =1+1 = 2

C[3,1] = C[2,1]+C[2,0] = 2+1 = 3

C[3,2] = C[2,2]+C[2,1] = 1+2 = 3

C[4,1] = C[3,0]+C[3,1] = 1+3 = 4


C[4,2] = C[3,1]+C[3,2] = 3 + 3 = 6

C[4,3] = C[3,2] + C[ 3,3] = 3 + 1 = 4


C[5,1] = C[4,0] + C[4,1] = 1 + 4 = 5
C[5,2] = C[4,1] + C[4,2] = 4 + 6 =10
C[5,3] = C[4,2] + C[4,3] = 6+ 4 = 10

C[5,4] = C[4,3] + C[4,4] = 4 + 1 =5


C[6,1] = C[5,0] + C[5,1] = 1 + 5 = 6
C[6,2] = C[5,1] + C[5,2] = 5 + 10= 15
C[6,3] = C[5,2] + C[5,3] = 10 +10=20

C[6,4] = C[5,3] + C[5,4] = 10 + 5 =15

Initial table: First column value and the diagonal is 1 based on first condition. Use the second formula to find
remaining values below diagonal.

r→ 0 1 2 3 4
n↓
0 1
1 1 1 C(6,4) =15
2 1 2 1
3 1 3 3 1
4 1 4 6 4 1
5 1 5 10 10 5
6 1 6 15 20 15

12. Explain Warshall’s algorithm to compute the transitive closure of a given graph with a suitable example.
Warshall Algorithm:
Warshall's algorithm is used to determine the transitive closure of a directed graph or all paths in a directed graph by using the
adjacency matrix.
Transitive Closure: Transitive closure is basically a Boolean matrix (with 0 and 1 values) in which the existence of directed paths
of arbitrary lengths between vertices is mentioned.

(Alternatively: all paths in a directed graph)

General formula:
rij(k) = rij(k-1)or (rik(k-1)and rkj(k-1))
Example:

13. Explain Floyd’s algorithm to find the shortest paths in a given graph with a detailed example.

The Floyd Warshall Algorithm is for solving all pairs of shortest-path problems. The problem is to
find the shortest distances between every pair of vertices in a given edge- weighted directed Graph. Floyd-
Warshall algorithm is also called as Floyd’s algorithm, Roy-Floyd algorithm, Roy- Warshall algorithm, or WFI
algorithm.

Algorithm:
For a graph with N vertices:
Step 1: Initialize the shortest paths between any 2 vertices with Infinity.
Step 2: Find all pair shortest paths that use 0 intermediate vertices, then find the shortest paths that use 1
intermediate vertex and so on.. until using all N vertices as intermediate nodes.
Step 3: Minimize the shortest paths between any 2 pairs in the previous operation.
Step 4: For any 2 vertices (i,j) , one should actually minimize the distances between this pair using the first K
nodes, so the shortest path will be: min(dist[i][k]+dist[k][j],dist[i][j]). dist[i][k] represents the shortest path that only
uses the first K vertices, dist[k][j] represents the shortest path between the pair k,j. As the shortest path will be a
concatenation of the shortest path from i to k, then from k to j.
for k in range(1,n):
for i in range(1,n):
for j in range(1,n):
dist[i][j] = min( dist[i][j], dist[i][k] + dist[k][j] )

In the above given algorithm the basic operation is:


Example:

14.Analyze the backtracking approach for solving the N-Queens problem and demonstrate its execution with an example.

Backtracking is a technique based on algorithm to solve problem. It uses recursive calling to find the solution by building a
solution step by step increasing values with time. It removes the solution that doesn’t give rise to the solution of the problem based
on the constraints given to solve the problem.
Applications of Backtracking:
 N Queens Problem
 Sum of subsets problem
 Graph colouring
 Hamiltonian cycles.
A backtracking algorithm is a problem-solving algorithm that uses a brute force approach for finding the desired output. The
Brute force approach tries out all the possible solutions and chooses the desired/best solutions.
The term backtracking suggests that if the current solution is not suitable, then backtrack and try other solutions. Thus, recursion is
used in this approach. This approach is used to solve problems that have multiple solutions.

The terms related to the backtracking are:


 Live node: The nodes that can be further generated are known as live nodes.
 E node: The nodes whose children are being generated and become a success node.
 Success node: The node is said to be a success node if it provides a feasible solution.
 Dead node: The node which cannot be further generated & does not provide a feasible solution is known as a dead node.
Example: N Queens Problem:
N - Queens problem is to place n - queens in such a manner on an n x n chessboard that no queens attack each other by being:
 In the same row,
 In the same column
 In the same diagonal.
If we take n=4 then the problem is called 4 queens problem.
If we take n=8 then the problem is called as 8 queens problem.

Algorithm for N queen problem:


Following is the backtracking algorithm for solving the N-Queen problem
1. Initialize an empty chessboard of size NxN.
2. Start with the leftmost column and place a queen in the first row of that column.
3. Move to the next column and place a queen in the first row of that column.
4. Repeat step 3 until either all N queens have been placed or it is impossible to place a queen in the current column without
violating the rules of the problem.
5. If all N queens have been placed, print the solution.
6. If it is not possible to place a queen in the current column without violating the rules of the problem, backtrack to the previous
column.
7. Remove the queen from the previous column and move it down one row.
8. Repeat steps 4-7 until all possible configurations have been tried.

Example: n=4  4-Queens problem:


Consider a 4*4 chessboard. Let there are 4 queens. The objective is place there 4 queens on 4*4 chessboard in such a way that no
two queens should be placed in the same row, same column or diagonal position.
The explicit constraints are 4 queens are to be placed on 4*4 chessboards in 44 ways.

15. Apply the Hamiltonian Circuit algorithm to solve a given problem and explain the solution with an example.

A Hamiltonian circuit or tour of a graph is a path that starts at a given vertex, visits each vertex in the graph exactly once,
and ends at the starting vertex. We use the Depth-First Search algorithm to traverse the graph until all the vertices have been
visited.
We traverse the graph starting from a vertex (arbitrary vertex chosen as starting vertex) and at any point during the traversal we get
stuck (i.e., all the neighbour vertices have been visited), we backtrack to find other paths (i.e., to visit another unvisited vertex).
If we successfully reach back to the starting vertex after visiting all the nodes, it means the graph has a Hamiltonian cycle
otherwise not.

Algorithms for Hamiltonian Circuits:


class Graph:
def init (self, vertices):
self.graph = [[0 for column in range(vertices)]
for row in range(vertices)]
self.V = vertices
# Check if this vertex can be added to the current path def
isSafe(self, v, pos, path):
# Check if this vertex is an adjacent vertex of the previously added vertex if
self.graph[path[pos - 1]][v] == 0:
return False
# Check if the vertex has already been included for
vertex in path:
if vertex == v:
return False
return True
# A recursive utility function to solve Hamiltonian cycle problem def
hamCycleUtil(self, path, pos):
if pos == self.V:
# Last vertex must be adjacent to the first vertex in path to make a cycle if
self.graph[path[pos - 1]][path[0]] == 1:
return True
else:
return False
# Remove current vertex if it doesn't lead to a solution
path[pos] = -1
return False
def hamCycle(self):
path = [-1] * self.V
# Start from vertex 0 as the first vertex in the path
path[0] = 0
if self.hamCycleUtil(path, 1) == False:
print("No Hamiltonian Cycle exists")
return False
self.printSolution(path)
return True
def printSolution(self, path):
print("Hamiltonian Cycle exists:")
for vertex in path:
print(vertex, end=" ")
# Complete the cycle
print(path[0], "\n")
# Example usage
g1 = Graph(5)
g1.graph = [[0, 1, 0, 1, 0],
[1, 0, 1, 1, 1],
[0, 1, 0, 0, 1],
[1, 1, 0, 0, 1],
[0, 1, 1, 1, 0]]
g1.hamCycle() g2
= Graph(5)
g2.graph = [[0, 1, 0, 1, 0],
[1, 0, 1, 1, 1],
[0, 1, 0, 0, 1],
[1, 1, 0, 0, 0],
[0, 1, 1, 0, 0]]
g2.hamCycle()

Example: Explain how to find Hamiltonian Cycle by using Backtracking in a given graph

Solution:
The backtracking approach uses a state-space tree to check if there exists a Hamiltonian cycle in the graph. Figure (f) shows the
simulation of the Hamiltonian cycle algorithm. For simplicity, we have not explored all possible paths, the concept is self-
explanatory.
Step 1: Tour is started from vertex 1. There is no path from 5 to 1. So it’s the dead-end state.
Step 2: Backtrack to the node from where the new path can be explored, that is 3 here
Step 3: New path also leads to a dead end so backtrack and explore all possible paths
Step 4: Next path is also leading to a dead-end so keep backtracking until we get some node that can generate a new path,
i.e .vertex 2 here
Step 5: One path leads to Hamiltonian cycle, next leads to a dead end so backtrack and explore all possible paths at each vertex
Step 6: Total two Hamiltonian cycles are detected in a given graph
16. Discuss the concept of Exhaustive Search and compare two different exhaustive search algorithms with examples.
EXHAUSTIVE SEARCH
Exhaustive search algorithms run in a realistic amount of time only on very small instances. Exhaustive search (or variation) is the
only known way to solve problem exactly for its entire possible instances TSP knapsack problem.
There are two common ways to traverse a graph, BFS and DFS. Considering a Tree (or Graph) of huge height and width, both BFS
and DFS are not very efficient due to some reasons.
1. BFS
Approach: BFS explores the graph level by level, starting from the source node. It visits all the nodes at one level before moving
to the next. BFS uses a queue to keep track of nodes that need to be explored.
How it works: From the source node, BFS moves to all its direct neighbors (Layer 1), then moves to the next layer (Layer 2),
continuing until all nodes are visited.

Traversal Order:
 Starting from node 0:
 Layer 0: Visit node 0 (source node).
 Layer 1: Visit nodes 1, 2, 3 (neighbors of node 0).
 Layer 2: Visit nodes 4, 5, 6, 7 (neighbors of nodes 1, 2, 3).
 Output: 0, 1, 2, 3, 4, 5, 6, 7.
2. DFS
Approach: DFS explores as deep as possible along a branch before backtracking. It uses a stack (or recursion) to keep track of
nodes and backtracks when it reaches a dead end.
How it works: Starting from the source node, DFS goes deeper into the graph, visiting one branch of the graph first
before backtracking to explore others.

Traversal Order:
 Starting from node 0:
 Visit node 0 → Visit node 1 → Visit node 4 (deepest) → Backtrack to node 1 → Visit node 5 → Backtrack to node 0 →
Visit node 2 → Visit node 6 → Backtrack to node 2 → Visit node 3 → Visit node 7.
 Output: 0, 1, 4, 5, 2, 6, 3, 7

ALGORITHM FOR BFS


from collections import defaultdict
class Graph:
def __init__(self):
self.graph = defaultdict(list)
def addEdge(self, u, v):
self.graph[u].append(v)
def BFS(self, s):
visited = [False] * (max(self.graph) + 1)
queue = []
queue.append(s)
visited[s] = True
while queue:
s = queue.pop(0)
print(s, end=" ")
for i in self.graph[s]:
if not visited[i]:
queue.append(i)
visited[i] = True
if __name__ == '__main__':
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)

print("Following is Breadth First Traversal"


" (starting from vertex 2)")
g.BFS(2)

ALGORITHM FOR DFS


from collections import defaultdict
class Graph:
def __init__(self):
self.graph = defaultdict(list)
def addEdge(self, u, v):
self.graph[u].append(v)
def DFSUtil(self, v, visited):
visited.add(v)
print(v, end=' ')
for neighbour in self.graph[v]:
if neighbour not in visited:
self.DFSUtil(neighbour, visited)
def DFS(self, v):
visited = set()
self.DFSUtil(v, visited)
if __name__ == "__main__":
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
print("Following is Depth First Traversal (starting from vertex 2)")
g.DFS(2)

17. Evaluate and compare BFS, DFS, and Iterative Deepening search techniques with appropriate algorithms and
examples.
Feature Breadth-First Search (BFS) Depth-First Search (DFS) Iterative Deepening Search
(IDS)
Search Strategy Level-wise exploration Depth-wise exploration Depth-wise with depth limits
(incremental DFS)
Data Structure Used Queue Stack / Recursion Stack + Depth Limit Control
Completeness Yes No (may go into infinite loop Yes
in infinite trees)
Optimality (Shortest Path) Yes (if all step costs are No Yes (if all step costs are
equal) equal)
Time Complexity O(b^d) O(b^d) O(b^d)
Space Complexity O(b^d) O(d) O(d)
Best Case When shortest path is required When searching deeper When memory is limited and
and memory is sufficient solutions with limited goal depth is unknown
memory
Worst Case High memory usage Infinite loop in cyclic or Redundant re-visiting of
infinite-depth graphs nodes in lower depths
Use Cases Shortest path problems, AI Game trees, maze solving, AI problems like 8-puzzle,
maps, network routing backtracking Chess Tree, Space-limited
search
Implementation Simplicity Moderate (requires queue) Simple (recursive or with More complex (requires DFS
stack) with depth limits)
Goal Detection Early if goal is at shallow Late if goal is deep Balanced due to incremental
level depth search
Example Traversal A→B→C→D→E A→B→D→C→E A → A,B → A,B,C →
A,B,C,D (until goal found)

BFS Algorithm:
BFS(start):
create an empty queue Q
mark start as visited
enqueue start into Q
while Q is not empty:
current = dequeue Q
for each neighbor of current:
if not visited:
mark neighbor as visited
enqueue neighbor

DFS Algorithm:
DFS(node, visited):
mark node as visited
for each neighbor of node:
if not visited:
DFS(neighbor, visited)

IDS Algorithm:
for depth = 0 to ∞:
result = DLS(start, depth)
if result found:
return result
DLS(node, limit):
if limit == 0:
return (goal check)
for each child of node:
DLS(child, limit - 1)

18. Explain the backtracking technique and demonstrate its application in solving combinatorial problems
Backtracking is a technique based on algorithm to solve problem. It uses recursive calling to find the solution by building a
solution step by step increasing values with time. It removes the solution that doesn’t give rise to the solution of the problem based
on the constraints given to solve the problem.
Applications of Backtracking:
 N Queens Problem
 Sum of subsets problem
 Graph colouring
 Hamiltonian cycles.
A backtracking algorithm is a problem-solving algorithm that uses a brute force approach for finding the desired output. The
Brute force approach tries out all the possible solutions and chooses the desired/best solutions.
The term backtracking suggests that if the current solution is not suitable, then backtrack and try other solutions. Thus, recursion is
used in this approach. This approach is used to solve problems that have multiple solutions.

The terms related to the backtracking are:


 Live node: The nodes that can be further generated are known as live nodes.
 E node: The nodes whose children are being generated and become a success node.
 Success node: The node is said to be a success node if it provides a feasible solution.
 Dead node: The node which cannot be further generated & does not provide a feasible solution is known as a dead node.
Example: N Queens Problem:
N - Queens problem is to place n - queens in such a manner on an n x n chessboard that no queens attack each other by being:
 In the same row,
 In the same column
 In the same diagonal.
If we take n=4 then the problem is called 4 queens problem.
If we take n=8 then the problem is called as 8 queens problem.

Algorithm for N queen problem:


Following is the backtracking algorithm for solving the N-Queen problem
1. Initialize an empty chessboard of size NxN.
2. Start with the leftmost column and place a queen in the first row of that column.
3. Move to the next column and place a queen in the first row of that column.
4. Repeat step 3 until either all N queens have been placed or it is impossible to place a queen in the current column without
violating the rules of the problem.
5. If all N queens have been placed, print the solution.
6. If it is not possible to place a queen in the current column without violating the rules of the problem, backtrack to the previous
column.
7. Remove the queen from the previous column and move it down one row.
8. Repeat steps 4-7 until all possible configurations have been tried.

Example: n=4  4-Queens problem:


Consider a 4*4 chessboard. Let there are 4 queens. The objective is place there 4 queens on 4*4 chessboard in such a way that no
two queens should be placed in the same row, same column or diagonal position.
The explicit constraints are 4 queens are to be placed on 4*4 chessboards in 44 ways.

19. Explain the Breadth-First Search (BFS) algorithm and illustrate its execution with an example.

 Breadth-first search is the most common search strategy for traversing a tree or graph. This algorithm
searches breadthwise in a tree or graph, so it is called breadth-first search.
 BFS algorithm starts searching from the root node of the tree and expands all successor node at the
current level before moving to nodes of next level.
 The breadth-first search algorithm is an example of a general-graph search algorithm.
 Breadth-first search implemented using FIFO queue data structure.

Algorithm for BFS:


from collections import defaultdict, deque class
Graph:
def init (self):
self.graph = defaultdict(list)
def add_edge(self, u, v):
self.graph[u].append(v)
def bfs(self, start):
visited = set()
queue = deque([start])
visited.add(start) while
queue:
node = queue.popleft() print(node,
end=" ")
for neighbor in self.graph[node]: if
neighbor not in visited:
queue.append(neighbor)
visited.add(neighbor)
# Example usage:
# Create a graph g = Graph()
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)
g.add_edge(3, 3)
print("Breadth First Traversal starting from vertex 2:") g.bfs(2)

BFS Algorithm Steps:

1. Start from the root node A and enqueue it.


2. Dequeue a node, visit it, and enqueue all its unvisited adjacent nodes.
3. Repeat step 2 until the queue is empty.

BFS Traversal (starting from A): Visited Order: A → B → C → D → E → F


Explanation:
 Start with A: Queue = [A]

 Visit A: Add B and C → Queue = [B, C]

 Visit B: Add D and E → Queue = [C, D, E]

 Visit C: Add F → Queue = [D, E, F]

 Visit D → Queue = [E, F]

 Visit E → Queue = [F]

 Visit F → Queue = []

20. Explain the Depth-First Search (DFS) algorithm and describe its working with an example.

 Depth-first search isa recursive algorithm for traversing a tree or graph data structure.
 It is called the depth-first search because it starts from the root node and follows each path to its greatest
depth node before moving to the next path.
 DFS uses a stack data structure for its implementation.
 The process of the DFS algorithm is similar to the BFS algorithm

Algorithm for Depth-first Search


from collections import defaultdict class
Graph:
def init (self):
self.graph = defaultdict(list)
def add_edge(self, u, v):
self.graph[u].append(v)
def dfs_util(self, v, visited):
visited.add(v)
print(v, end=" ")
for neighbor in self.graph[v]: if
neighbor not in visited:
self.dfs_util(neighbor, visited)
def dfs(self, start):
visited = set()
self.dfs_util(start, visited)
# Example usage:
# Create a graph g = Graph()
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)
g.add_edge(3, 3)
print("Depth First Traversal starting from vertex 2:") g.dfs(2)

DFS

Vertices: A, B, C, D, E, F, G, H
Edges:
 A → B, A → C
 B→G
 C→A
 D → A, D → C
 E→H
 G → E, G → F
 H→B
DFS Traversal (starting from A): We’ll use a recursive DFS approach (or simulate it with a stack), exploring as far as possible
before backtracking.
Let’s perform DFS starting from node A:

1. A → Visit A
2. B → Move to B from A
3. G → Move to G from B
4. E → Move to E from G
5. H → Move to H from E
6. F → Backtrack to G, move to F
7. C → Backtrack to A, now move to C
8. D → Move to D from C (if unvisited, but D already visited through C and A)

Final DFS Traversal Order: A → B → G → E → H → F → C → D

UNIT- V - BRANCH-AND-BOUND, NP PROBLEMS AND APPROXIMATION ALGORITHMS


11. Explain the Assignment Problem and demonstrate how the Branch and Bound method can be applied to solve it with a
suitable example.
Assignment Problem:
 Given n tasks and n agents.
 Each agent has a cost to complete each task.
 Assign each agent a task to minimize cost.
Example:
12. Apply the Branch and Bound algorithm to solve the given Traveling Salesman Problem (TSP) and analyze
the optimal path.
A B C D E
A 0 3 1 5 8
B 3 0 6 7 9
C 1 6 0 4 2
D 5 7 4 0 3
E 8 9 2 3 0

Traveling salesman problem:


Travelling Salesman Problem (TSP) Problem is defined as “given n cities and distance between each pair of cities, find out the path
which visits each city exactly once and come back to starting city, with the constraint of minimizing the travelling distance.”

Algorithm:
1. Initially, graph is represented by cost matrix C, where
 Cij = cost of edge, if there is a direct path from city i to city j
 Cij = ∞, if there is no direct path from city i to city j.
2. Convert cost matrix to reduced matrix by subtracting minimum values from appropriate rows and columns, such that each row
and column contains at least one zero entry.
3. Find cost of reduced matrix. Cost is given by summation of subtracted amount from the cost matrix to convert it in to reduce
matrix.
4. Prepare state space tree for the reduce matrix
5. Find least cost valued node A (i.e. E-node), by computing reduced cost node matrix with every remaining node.
6. If <i, j> edge is to be included, then do following:
(a) Set all values in row i and all values in column j of A to ∞
(b) Set A[j, 1] = ∞
(c) Reduce A again, except rows and columns having all ∞ entries.
7. Compute the cost of newly created reduced matrix as, Cost = L + Cost(i, j) + r
Where, L is cost of original reduced cost matrix and r is A[i, j].
8. If all nodes are not visited then go to step 4.
13. Apply the Branch and Bound approach to solve the given Knapsack Problem instance. Explain the step-by-
step execution of the algorithm.
Item Weight value
1 3 $25
2 2 $20
3 1 $15
4 4 $40
5 5 $50
Capacity w=6
14.Define P, NP, and NP-Complete problems. Provide examples for each category and explain their significance
in computational complexity.

P Class
The P in the P class stands for Polynomial Time. It is the collection of decision problems (problems with a “yes” or “no”
answer) that can be solved by a deterministic machine (our computers) in polynomial time.
Features:
 The solution to P problems is easy to find.
 P is often a class of computational problems that are solvable and tractable.
Most of the coding problems that we solve fall in this category like the below.
1. Calculating the greatest common divisor.
2. Finding a maximum matching.
3. Merge Sort

NP Class
The NP in NP class stands for Non-deterministic Polynomial Time. It is the collection of decision problems that can be solved
by a non-deterministic machine (note that our computers are deterministic) in polynomial time.
Features:
 The solutions of the NP class might be hard to find since they are being solved by a non-deterministic machine but the
solutions are easy to verify.
 Problems of NP can be verified by a deterministic machine in polynomial time.
This class contains many problems that one would like to be able to solve effectively:
1. Boolean Satisfiability Problem (SAT).
2. Hamiltonian Path Problem.
3. Graph coloring.

NP-hard class
An NP-hard problem is at least as hard as the hardest problem in NP and it is a class of problems such that every problem in NP
reduces to NP-hard.
Features:
 All NP-hard problems are not in NP.
 It takes a long time to check them. This means if a solution for an NP-hard problem is given then it takes a long time to
check whether it is right or not.
 A problem A is in NP-hard if, for every problem L in NP, there exists a polynomial-time reduction from L to A.
Some of the examples of problems in Np-hard are:
1. Halting problem.
2. Qualified Boolean formulas.
3. TSP

NP-complete class
A problem is NP-complete if it is both NP and NP-hard. NP-complete problems are the hard problems in NP.
Features:
 NP-complete problems are special as any problem in NP class can be transformed or reduced into NP-complete problems in
polynomial time.
 If one could solve an NP-complete problem in polynomial time, then one could also solve any NP problem in polynomial
time.
Some example problems include:
1. Hamiltonian Cycle.
2. Satisfiability.
3. Vertex cover.
Class Definition Example Solving Time
P Solvable in polynomial time Sorting, Searching Fast and efficient
Hard to solve, easy to
NP Verifiable in polynomial time Sudoku, Subset Sum
verify
3-SAT, TSP (Decision),
NP-Complete In NP and as hard as any NP problem No known fast solution
Knapsack

15. What are approximation algorithms, and how can they be used to solve the Knapsack Problem? How do they differ
from exact algorithms?
Definition:
Approximation algorithms are algorithms designed to find near-optimal solutions to optimization problems (especially NP-Hard
problems) within a guaranteed bound of the optimal solution, and in polynomial time.
They are used when exact algorithms are too slow or computationally infeasible for large inputs.

Approximation Algorithms for Knapsack problem


├── Greedy Algorithm
└── Approximation Schemes

1. Greedy Algorithm for Knapsack problem:

i. Compute value/weight ratio vi / wi for all items.


ii. Sort the items in non-increasing order of the ratios vi / wi
iii. Repeat until no item is left in the sorted list using the following steps:
a) If the current item fits, use it.
b) Otherwise, skip this item and proceed to the next item.
2. Approximation Scheme:
i. Scales item values to reduce problem size,
ii. Uses dynamic programming on scaled values,
iii. Achieves solution within (1 - ε) of the optimal value, where ε is a small positive number.

Difference from Exact Algorithms

Feature Exact Algorithm Approximation Algorithm


Output Always optimal Near-optimal (within known bound)
Time Complexity May be exponential (e.g., brute-force, dynamic Polynomial (for many approximations)
programming)
Usage Small input sizes or critical accuracy Large inputs, time-critical problems
Guarantee Finds best possible solution Solution is within a factor of optimal

Example: knapsack problem


16. Discuss the concept of NP-Hard problems and explain how the Travelling Salesman Problem (TSP) is classified as NP-
Hard. Provide an example with justification.

NP-hard class
An NP-hard problem is at least as hard as the hardest problem in NP and it is a class of problems such that every problem in NP
reduces to NP-hard.
Features:
 All NP-hard problems are not in NP.
 It takes a long time to check them. This means if a solution for an NP-hard problem is given then it takes a long time to
check whether it is right or not.
 A problem A is in NP-hard if, for every problem L in NP, there exists a polynomial-time reduction from L to A.
Some of the examples of problems in Np-hard are:
i. Halting problem.
ii. Qualified Boolean formulas.
iii. TSP

Problem Statement:
Given a list of cities and the distances between each pair of cities, the goal is to find the shortest possible route that visits each city
exactly once and returns to the origin city.
Classification:
 The optimization version of TSP (find the shortest path) is NP-Hard.
 The decision version of TSP (is there a tour with total cost ≤ k?) is NP-Complete.

The optimization TSP is NP-Hard because:


 It is at least as hard as any NP problem.
 Any instance of an NP-Complete problem can be reduced to TSP in polynomial time.

 There is no known polynomial-time algorithm to solve it exactly.

Example:
Let’s say you have 4 cities: A, B, C, D, and the distances between them are:
From → To A B C D
A - 2 9 10
B 1 - 6 4
C 15 7 - 8
D 6 3 12 -
We start from A and must return to A. The total routes are:
1. A → B → C → D → A
2. A → B → D → C → A
3. A → C → B → D → A
4. A → C → D → B → A
5. A → D → B → C → A
6. A → D → C → B → A
Calculate the Total Cost:
Let’s compute route 2:
A→B→D→C→A
 A→B=2
 B→D=4
 D → C = 12
 C → A = 15
Total = 2 + 4 + 12 + 15 = 33
Now route 1:
A→B→C→D→A
 A→B=2
 B→C=6
 C→D=8
 D→A=6
Total = 2 + 6 + 8 + 6 = 22
Route 1 is better (total cost = 22) than route 2 (cost = 33).
Out of all routes, the one with the minimum total cost is selected.
In this case: A → B → C → D → A with cost = 22 (optimal route)

17. What are Approximation Algorithms? Discuss the importance of Approximation Algorithms in solving NP-Hard
Problems, and illustrate with an example from Knapsack or TSP.
Definition:
Approximation algorithms are algorithms designed to find near-optimal solutions to optimization problems (especially NP-Hard
problems) within a guaranteed bound of the optimal solution, and in polynomial time.
They are used when exact algorithms are too slow or computationally infeasible for large inputs.

Approximation Algorithms for Knapsack problem


├── Greedy Algorithm
└── Approximation Schemes
1. Greedy Algorithm for Knapsack problem:

i. Compute value/weight ratio vi / wi for all items.


ii. Sort the items in non-increasing order of the ratios vi / wi
iii. Repeat until no item is left in the sorted list using the following steps:
a) If the current item fits, use it.
b) Otherwise, skip this item and proceed to the next item.

2. Approximation Scheme:
i. Scales item values to reduce problem size,
ii. Uses dynamic programming on scaled values,
iii. Achieves solution within (1 - ε) of the optimal value, where ε is a small positive number.

Importance of Approximation Algorithms in NP-Hard Problems:


1. Feasibility in Real Time:
o NP-Hard problems (e.g., TSP, Knapsack) can take exponential time.
o Approximation algorithms run in polynomial time, making them useful in practical applications like logistics,
scheduling, and data compression.
2. Predictable Performance:
o They provide provable bounds on how close the solution is to the optimal.
3. Scalability:
o They can handle large problem sizes that exact algorithms cannot.
4. Useful in Decision-Making:
o Even approximate solutions can guide real-world decision-making effectively.

Example: knapsack problem


18. Explain the Branch and Bound technique and apply it to solve a Knapsack Problem with a suitable example.

Branch-And-Bound:
Branch and Bound is another method to systematically search a solution space. Just like backtracking, we will use bounding
functions to avoid generating subtrees that do not contain an answer node.
Branch and Bound differs from backtracking in two important points:
 It has a branching function, it uses breadth first search
 It has a bounding function, which goes far beyond the feasibility test as a mean to prune efficiently the search tree.
Branch and Bound refers to all state space search methods in which all children of the E-node are generated before any other live
node becomes the E-node.
 Live node is a node that has been generated but whose children have not yet been generated.
 E-node is a live node whose children are currently being explored. In other words, an E-node is a node currently being
expanded.
 Dead node is a generated node that is not to be expanded or explored any further. All children of a dead node have
already been expanded.
 Branch-an-bound refers to all state space search methods in which all children of an E-node are generated before any
other live node can become the E-node.
Example:
19. Discuss hybrid approaches that combine Branch and Bound, Dynamic Programming, and Approximation Algorithms
to solve NP-Hard problems. Provide case studies from TSP or Knapsack.

Hybrid approaches that combine Branch and Bound (B&B), Dynamic Programming (DP), and Approximation
Algorithms to tackle NP-Hard problems like TSP (Travelling Salesman Problem) and Knapsack Problem.

Technique Strength
Branch and Bound Prunes the search space using bounds
Dynamic Programming Breaks down problems into overlapping subproblems
Approximation Algorithms Provides fast, near-optimal solutions for large input sizes

NP-Hard problems can’t be solved exactly in polynomial time. Hybrid methods:


 Handle large inputs more efficiently
 Use heuristics or bounds to guide search
 Avoid full enumeration by eliminating suboptimal paths

Case Study 1: TSP – Hybrid B&B + DP + Approximation


Problem:
Given n cities and distances, find the shortest route that visits all cities once and returns to the start.

Hybrid Strategy:
1. Initial Approximate Solution:
o Use Nearest Neighbour (NN) to get a starting tour and cost
o This acts as an upper bound for B&B
2. Branch and Bound:
o Use this to prune branches that exceed the current best cost
3. Dynamic Programming:
o Use memorization for optimal substructures to reduce computation when visiting cities

Benefits:
 Approximation gives quick upper bounds
 B&B prunes unnecessary paths
 DP reduces repeated computation
Result:
 Much faster than using any one technique alone
 Scales better for 20–30 cities range

Case Study 2: 0/1 Knapsack – Hybrid B&B + DP + Approximation

Problem:
Given items with weights and values, select items to maximize value without exceeding weight limit.

Hybrid Strategy:
1. Approximation Algorithm:
o Gives quick near-optimal solution → Used as initial upper bound
2. Dynamic Programming Table:
o Pre-compute solutions to subproblems up to weight limit
3. Branch and Bound:
o Explore only promising item combinations based on value-to-weight ratio
o Use DP table values for pruning bounds

Result:
 Avoids full state space exploration
 Works for medium-large item sets (n ≈ 100–1000)

Comparison Table

Feature Pure B&B DP Approx. Hybrid


Time High Medium Low Balanced
Accuracy Exact Exact Near-optimal Exact or near-optimal
Scalability Poor Medium High Improved

Conclusion

Hybrid approaches are essential in real-world applications where:


 Input sizes are large
 Time constraints exist
 Optimality is desirable but not always feasible
They combine exploration, memory optimization, and heuristic power, making them a robust strategy for NP-Hard problems.

20.Find the solution of following travelling salesman problem using branch and
bound method.
Cost Matrix ∞ 20 30 10 11 =
15 ∞ 16 4 2
3 5 ∞ 2 4
19 6 18 ∞ 3
16 4 7 16 ∞

Traveling salesman problem:


Travelling Salesman Problem (TSP) Problem is defined as “given n cities and distance between each pair of cities,
find out the path which visits each city exactly once and come back to starting city, with the constraint of
minimizing the travelling distance.”

Algorithm:
1. Initially, graph is represented by cost matrix C, where
 Cij = cost of edge, if there is a direct path from city i to city j
 Cij = ∞, if there is no direct path from city i to city j.
2. Convert cost matrix to reduced matrix by subtracting minimum values from appropriate rows and columns,
such that each row and column contains at least one zero entry.
3. Find cost of reduced matrix. Cost is given by summation of subtracted amount from the cost matrix to convert
it in to reduce matrix.
4. Prepare state space tree for the reduce matrix
5. Find least cost valued node A (i.e. E-node), by computing reduced cost node matrix with every remaining
node.
6. If <i, j> edge is to be included, then do following:
(a) Set all values in row i and all values in column j of A to ∞
(b) Set A[j, 1] = ∞
(c) Reduce A again, except rows and columns having all ∞ entries.
7. Compute the cost of newly created reduced matrix as, Cost = L + Cost(i, j) + r
Where, L is cost of original reduced cost matrix and r is A[i, j].
8. If all nodes are not visited then go to step 4.

Solution:
Reduce above cost matrix by subtracting minimum value from each row and column.
Reduce it subtracting minimum value from corresponding column. Doing this we get,
Cost of M1 = C(1)
= Row reduction cost + Column reduction cost
= (10 + 2 + 2 + 3 + 4) + (1 + 3) = 25
This means all tours in graph has length at least 25. This is the optimal cost of the path.

You might also like