Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
10 views

Cahcet Updated Algorithm Lab Manual

Algorithm

Uploaded by

revathipugazhe
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Cahcet Updated Algorithm Lab Manual

Algorithm

Uploaded by

revathipugazhe
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 63

LAB MANUAL

ALGORITHMS LABORATORY

SUBJECT CODE: CS 3401

IV SEMESTER – CSE
(As Per ANNA UNIVERSITY Syllabus Regulations
2021)

PREPARED BY APPROVED BY

Mrs.P. REVATHI, M.E., HOD/CSE

DEPARTMENT OF COMPUTER SCIENCE & ENGINEERING

C. ABDUL HAKEEM COLLEGE OF ENGINEERING & TECHNOLOGY


(Accredited by NBA and ISO 9001:2008 certified institution)
Hakeem Nagar, Melvisharam – 632 509.
Vellore District Tamil Nadu,
India.
www.cahcet.in

SYLLABUS
PRACTICAL EXERCISES
Searching and Sorting Algorithms
1. Implement Linear Search. Determine the time required to search for an element.
Repeat the experiment for different values of n, the number of elements in the list to
be searched and plot a graph of the time taken versus n.
2. Implement recursive Binary Search. Determine the time required to search an
element. Repeat the experiment for different values of n, the number of elements in
the list to be searched and plot a graph of the time taken versus n.
3. Given a text txt [0...n-1] and a pattern pat [0...m-1], write a function search (char pat [
], char txt [ ]) that prints all occurrences of pat [ ] in txt [ ]. You may assume that n >
m.
4. Sort a given set of elements using the Insertion sort and Heap sort methods and
determine the time required to sort the elements. Repeat the experiment for different
values of n, the number of elements in the list to be sorted and plot a graph of the time
taken versus n.
Graph Algorithms
1. Develop a program to implement graph traversal using Breadth First Search
2. Develop a program to implement graph traversal using Depth First Search
3. From a given vertex in a weighted connected graph, develop a program to find the shortest
paths
to other vertices using Dijkstra’s algorithm.
4. Find the minimum cost spanning tree of a given undirected graph using Prim’s algorithm.
5. Implement Floyd’s algorithm for the All-Pairs- Shortest-Paths problem.
6. Compute the transitive closure of a given directed graph using Warshall's algorithm.
Algorithm Design Techniques
1. Develop a program to find out the maximum and minimum numbers in a given list of
n numbers using the divide and conquer technique.
2. Implement Merge sort and Quick sort methods to sort an array of elements and
determine the time required to sort. Repeat the experiment for different values of n,
the number of elements in the list to be sorted and plot a graph of the time taken
versus n.
State Space Search Algorithms
1. Implement N Queens problem using Backtracking.

Approximation Algorithms Randomized Algorithms


1. Implement any scheme to find the optimal solution for the Traveling Salesperson
problem and then solve the same problem instance using any approximation
algorithm and determine the error in the approximation.
2. Implement randomized algorithms for finding the kth
smallest number.

The programs can be implemented in C/C++/JAVA/ Python.


TOTAL:30
PERIODS

COURSE OUTCOMES:
At the end of this course, the students will be able to:
CO1: Analyze the efficiency of algorithms using various frameworks
CO2: Apply graph algorithms to solve problems and analyze their efficiency.
CO3: Make use of algorithm design techniques like divide and conquer, dynamic
programming and greedy techniques to solve problems
CO4: Use the state space tree method for solving problems.
CO5: Solve problems using approximation algorithms and randomized algorithms
CO’s- PO’s & PSO’s MAPPING
CO’s PO’s PSO’s
1 2 3 4 5 6 7 8 9 10 11 12 1 2 3
1 2 1 3 2 - - - - 2 1 2 3 2 1 1
2 2 1 1 1 1 - - - 1 3 3 3 2 3 3
3 1 3 3 3 1 - - - 1 2 1 2 2 1 2
4 1 2 2 3 - - - - 2 3 3 1 3 1 3
5 1 2 3 2 3 - - - 3 1 3 3 1 3 3
AVg. 1 2 2 2 2 - - - 2 2 2 2 2 2 2

1 - low, 2 - medium, 3 - high, ‘-“- no correlation

EXP.NO:1 IMPLEMENTATION OF LINEAR SEARCH


DATE:
AIM : To Implement Linear Search. Determine the time required to search for an element. Repeat the
experiment for different values of n, the number of elements in the list to be searched and plot a graph
of the time taken versus n.
ALGORITHM :
1. Declare an array.
2. The linear_search function takes an array arr and an element x as input, and searches for
the element in the array using linear search.
3. If the element is found, it returns the index of the element in the array. Otherwise, it
returns -1.
4. The program defines a list n_values containing different values of n to test the linear search
algorithm on.
5. It then loops through this list, generates a random list of n elements, and searches for a
random element in the list.
6. It measures the time taken to perform the search using the time module, and appends the
time taken to a list time_values.
7. Finally, the program uses matplotlib library to plot a graph of the time taken versus n.
PROGRAM:
import time
import matplotlib.pyplot as plt
import random
def linear_search(arr, x):
for i in range(len(arr)):
if arr[i] == x:
return i
return -1
n_values = [100, 1000, 10000, 100000, 1000000]
time_values = []
for n in n_values:
arr = [random.randint(0, n) for _ in range(n)]
x = random.randint(0, n)

start_time = time.time()
linear_search(arr, x)
end_time = time.time()

# Append the time taken for this iteration


time_values.append(end_time - start_time)
# Plot the results
plt.plot(n_values, time_values, marker='o')
plt.title('Linear Search Performance')
plt.xlabel('Number of Elements')
plt.ylabel('Time Taken (seconds)')
plt.grid()
plt.show()

OUTPUT:
Output 1:
n_values = [100, 1000, 10000, 100000, 1000000]

Output 2 :
n_values = [10, 100, 1000, 1, 10000]
Result: Thus the python program for implementation of linear search program was executed and
verified successfully.

VIVA QUESTIONS

1.What is the time complexity of Linear Search?


2.Can Linear Search be applied to both sorted and unsorted data? Why?
3.What is the space complexity of Linear Search?
4.What are the advantages and disadvantages of Linear Search?
5.What is the impact of input size on the performance of Linear Search?

EXP.NO:2 IMPLEMENTATION OF RECURSIVE BINARY SEARCH


DATE:
AIM :
To Implement recursive Binary Search. Determine the time required to search an element.
Repeat the experiment for different values of n, the number of elements in the list to be searched
and plot a graph of the time taken versus n.
ALGORITHM :
1. Declare the array.
2. ‘binary_search_recursive’ is a recursive function that takes an array ‘arr’, the lower and
upper bounds of the subarray being searched ‘low ‘and ‘high', and the element being searched
for ‘x’.
3. It returns the index of the element if it is found, or -1 if it is not found.
4. The function ‘test_binary_search_recursive’ generates arrays of different sizes and runs a
binary search for a random element in each array.
5. It records the time taken to run the search and plots it on a graph.
6. The graph shows the time taken to search for an element versus the size of the array being
searched.
7. As the size of the array increases, the time taken to search for an element increases as well, but
the increase is logarithmic since binary search has a time complexity of O(log n).
PROGRAM :
import random
import time
import matplotlib.pyplot as plt
def binary_search_recursive(arr, low, high, x):
if high >= low:
mid = (high + low) // 2

if arr[mid] == x:
return mid

elif arr[mid] > x:


return binary_search_recursive(arr, low, mid - 1, x)

else:
return binary_search_recursive(arr, mid + 1, high, x)

else:
return -1
def test_binary_search_recursive():
for n in [10, 100, 1000, 10000, 100000]:
arr = [random.randint(1, n) for i in range(n)]
arr.sort()
start_time = time.time()
x = random.randint(1, n)
result = binary_search_recursive(arr, 0, n-1, x)
end_time = time.time()

if result == -1:
print(f"Element {x} not found in the array")
else:
print(f"Element {x} found at index {result}")

print(f"Time taken to search in array of size {n}: {end_time - start_time}")


print("=" * 50)
plt.scatter(n, end_time - start_time)
plt.title("Recursive Binary Search Performance")
plt.xlabel("Size of Array")
plt.ylabel("Time Taken (in seconds)")
plt.show()
test_binary_search_recursive()

OUTPUT:
Element 4 not found in the array
Time taken to search in array of size 10: 7.3909759521484375e-06
==================================================
Element 31 found at index 36
Time taken to search in array of size 100: 7.867813110351562e-06
==================================================
Element 414 found at index 393
Time taken to search in array of size 1000: 1.9311904907226562e-05
==================================================
Element 4378 not found in the array
Time taken to search in array of size 10000: 4.673004150390625e-05
==================================================
Element 52551 found at index 52435
Time taken to search in array of size 100000: 4.482269287109375e-05
=======================================
Result:
Thus the python program for implementation of recursive binary search was executed and
verified successfully.

VIVA QUESTIONS
1.How does Recursive Binary Search differ from Iterative Binary Search?
2.What is the time complexity of Recursive Binary Search?
3.How do you calculate the middle index in Binary Search, and why is integer overflow a concern?
4.What happens when the search space reduces to zero?
5.What are the advantages and disadvantages of Recursive Binary Search?

EXP.NO: 3 PATTERN MATCHING


DATE:
AIM :
To implement all occurrences of pat [ ] in txt [ ]. You may assume that n > m. Given a text txt
[0...n-1] and a pattern pat [0...m-1], write a function search (char pat [ ], char txt [ ])
ALGORITHM :
1. One way to implement the search function is to use the brute-force approach, which involves
comparing each possible substring of the text with the pattern.
2. The algorithm iterates through the text from the first character to the (n-m)th character and
checks whether the pattern matches the substring of the text starting at that position.
3. If a match is found, the function prints the index of the match.
PROGRAM:
def search(pat, txt):
n = len(txt)
m = len(pat)
result = []

# Loop through the text and search for the pattern


for i in range(n - m + 1):
j=0
while j < m:
if txt[i + j] != pat[j]:
break
j += 1

# If the entire pattern is found, add the index to the result list
if j == m:
result.append(i)

return result

txt = "AABAACAADAABAABA"
pat = "AABA"
result = search(pat, txt)
print("Pattern found at indices:", result)

OUTPUT:
Pattern found at indices: [0, 9, 12]

Result: Thus the python program implementation of pattern matching was executed and verified
Successfully.

VIVA QUESTIONS

1.What is the time complexity of the Naive Pattern Matching algorithm?


2.How does the input size impact the performance of pattern matching algorithms?
3.What happens if the pattern is an empty string?
4.What are the advantages and disadvantages of Linear Search?
5.What are some real-world applications of pattern matching?
EXP.NO: 4(a) IMPLEMENTATION OF INSERTION SORT
DATE:
AIM :
To Sort a given set of elements using the Insertion sort and Heap sort methods and determine
the time required to sort the elements. Repeat the experiment for different values of n, the number of
elements in the list to be sorted and plot a graph of the time taken versus n.
ALGORITHM:
Algorithm for insertion sort:
1. The insertion Sort function takes a list of elements and sorts them using the Insertion sort algorithm.
2.The generate List function generates a list of n random numbers between 1 and 1000.
3.The measure Time function generates a list of n random numbers, sorts it using the insertion Sort
function, and measures the time required to sort the list.
4.The plot Graph function generates a list of n values and calls the measure Time function for each n
value. It then plots a graph of the time required to sort the list versus the value of n.
PROGRAM:INSERTION SORT
import matplotlib.pyplot as plt
import random
import time

def insertionSort(arr):
n = len(arr)
for i in range(1, n):
key = arr[i]
j=i-1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key

# Generate a list of n random numbers


def generateList(n):
return [random.randint(1, 1000) for i in range(n)]

# Measure the time required to sort a list of n elements


def measureTime(n):
arr = generateList(n)
startTime = time.time()
insertionSort(arr)
endTime = time.time()
return endTime - startTime

# Plot a graph of the time required to sort a list of n elements


def plotGraph(nList):
timeList = [measureTime(n) for n in nList]
plt.plot(nList, timeList, 'o-')
plt.xlabel('n')
plt.ylabel('Time (s)')
plt.title('Insertion Sort')
plt.show()
nList = [100, 500, 1000, 2000, 5000, 10000]
plotGraph(nList)

OUTPUT:

RESULT: Thus the . python program for implementation of insertion sort and heap sort was executed
and verified successfully.

VIVA QUESTIONS
1.What is Insertion Sort?
2.What is the best-case, worst-case, and average-case time complexity of Insertion Sort?
3.What is the space complexity of Insertion Sort?
4.Can Insertion Sort be implemented recursively? If yes, how?
5.How does Insertion Sort handle duplicate elements?

EX. NO: 4(B) IMPLEMENTATION OFHEAP SORT


DATE:
AIM:
To Develop a program to implement Heap sort and determine the time required to sort
the elements.
ALGORITHM:
1. Build a max heap from the given array
2. Swap the root element with the last element of the array
3. Remove the last element from the heap (it is now in its correct position)
4. Heapify the remaining array to maintain the heap property
5. Repeat steps 2-4 until the entire array is sorted
PROGRAM:
import random
import time
import matplotlib.pyplot as plt
def heapify(arr, n, i):
largest = i
l=2*i+1
r=2*i+2
if l < n and arr[largest] < arr[l]:
largest = l
if r < n and arr[largest] < arr[r]:
largest = r
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i]
heapify(arr, i, 0)
n_values = [1000, 2000, 3000, 4000, 5000]
times = []
for n in n_values:
arr = [random.randint(1, 10000) for _ in range(n)]
start_time = time.time()
heap_sort(arr)
end_time =
time.time()
times.append(end_time - start_time)
plt.plot(n_values, times)
plt.xlabel('n')
plt.ylabel('Time (s)')
plt.title('Heap sort time complexity')
plt.show()
Output:
n=1000: 0.07786297798156738 seconds
n=2000: 0.32976579666137695 seconds
n=3000: 0.7329976558685303 seconds
n=4000: 1.3388376235961914 seconds
n=5000: 2.0903823375701904 seconds

Result:
Thus, the program has been executed successfully and the output has been verified.

VIVA QUESTIONS

1.What type of data structure does Heap Sort use?


2.What is the time complexity of Heap Sort?
3.What is the space complexity of Heap Sort?
4.What is the purpose of the "build-heap" function in Heap Sort?
5.What is the difference between a max-heap and a min-heap?
EXP.NO: 5 IMPLEMENTATION OF GRAPH TRAVERSAL USING BREADTH FIRST
SEARCH
DATE:
AIM :
To develop a program to implement graph traversal using Breadth First Search.
ALGORITHM:
Step 1:Start by putting any one of the graph's vertices at the back of a queue.
Step 2:Take the front item of the queue and add it to the visited list.
Step 3:Create a list of that vertex's adjacent nodes. Add the ones which aren't in the visited list to the
back of the queue.
Step 4:Keep repeating steps 2 and 3 until the queue is empty.

PROGRAM:
import networkx as nx
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
G = nx.Graph(graph)
nx.draw(G, with_labels = True)
visited = [] # List for visited nodes.
queue = [] #Initialize a queue

def bfs(visited, graph, node): #function for BFS


visited.append(node)
queue.append(node)
# Creating loop to visit each node
while queue:
m = queue.pop(0)
print (m, end = " ")

for neighbour in graph[m]:


if neighbour not in visited:
visited.append(neighbour)
queue.append(neighbour)

# Driver Code
print("Following is the Breadth-First Search")
bfs(visited, graph, '5') # function calling
OUTPUT:
Following is the Breadth-First Search
537248

RESULT: Thus the python program for implementation of graph traversal using breadth first search was
executed and verified successfully.

VIVA QUESTIONS
1.What is the time complexity of BFS?
2.What is the space complexity of BFS?
3.What happens if the graph contains a cycle?
4.What happens if the graph is empty?
5.Why is BFS not suitable for very large graphs in memory-constrained systems?
EXP.NO: 6 IMPLEMENTATION OF GRAPH TRAVERSAL USING DEPTH FIRST
SEARCH
DATE:
AIM :
To develop a program to implement graph traversal using Depth First Search.
ALGORITHM :
Step 1: Start by putting any one of the graph's vertices on top of a stack.
Step 2:Take the top item of the stack and add it to the visited list.
Step 3:Create a list of that vertex's adjacent nodes. Add the ones which aren't in the visited list to
the top of the stack.
Step 4:Keep repeating steps 2 and 3 until the stack is empty.
PROGRAM :
# Using adjacency list
g={
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
G = nx.Graph(g)
nx.draw(G, with_labels = True)
visited = set()
# Set to keep track of visited nodes of graph.

def dfs(visited, g, node): dfs


if node not in visited:
print (node)
visited.add(node)
for neighbour in g[node]:
dfs(visited, g, neighbour)
# Driver Code
print("Following is the Depth-First Search")
dfs(visited, g, '5')
OUTPUT:
Following is the Depth-First Search
5
3
2
4
8
7

RESULT: Thus the python program for implementation of graph traversal using Depth first search was
executed and verified successfully.
VIVA QUESTIONS
1.What is Depth-First Search (DFS), and how does it work?
2.What data structures are used in the iterative and recursive implementations of DFS?
3.What is the time complexity of DFS?
4.How does DFS handle cycles in a graph?
5.What are some real-world applications of DFS?

EXP.NO: 7 IMPLEMENTATION OF DIJIKSTRA’S ALGORITHM


DATE:

AIM: To develop a program to find the shortest paths to other vertices using Dijkstra’s algorithm.

ALGORITHM :
1.First, we define a function ‘dijkstra’ that takes three arguments: the graph represented as an adjacency
matrix, the starting vertex src, and the number of vertices in the graph n.
2.The function returns a list of shortest distances from the source vertex to all other vertices in the
graph.

PROGRAM:
# importing network
import networkx as nx
import pylab
import matplotlib.pyplot as plt

# Create an empty Undirected Weighted Graph


G = nx.Graph()

nodes_list = [1, 2, 3, 4, 5, 6, 7]
G.add_nodes_from(nodes_list)

# Add weighted edges


edges_list = [(1, 2, 13),(1, 4, 4),(2, 3, 2),(2, 4, 6), (2, 5, 4), (3, 5, 5),
(3, 6, 6),(4, 5, 3),(4, 7, 4), (5, 6, 8), (5, 7, 7), (6, 7, 3)]
G.add_weighted_edges_from(edges_list)
plt.figure()
pos = nx.spring_layout(G)
weight_labels = nx.get_edge_attributes(G,'weight')

nx.draw(G,pos,font_color = 'white', node_shape = 's', with_labels = True,)


nx.draw_networkx_edge_labels(G,pos,edge_labels=weight_labels)

pos = nx.planar_layout(G)

#Give us the shortest paths from node 1 using the weights from the edges.
p1 = nx.shortest_path(G, source=1, weight="weight")

# This will give us the shortest path from node 1 to node 6.


p1to6 = nx.shortest_path(G, source=1, target=6, weight="weight")

# This will give us the length of the shortest path from node 1 to node 6.
length = nx.shortest_path_length(G, source=1, target=6, weight="weight")

print("All shortest paths from 1: ", p1)


print("Shortest path from 1 to 6: ", p1to6)
print("Length of the shortest path: ", length)

OUTPUT:
All shortest paths from 1: {1: [1], 2: [1, 4, 2], 4: [1, 4], 5: [1, 4, 5], 7: [1, 4, 7], 3: [1, 4, 5, 3], 6: [1, 4, 7,
6]}
Shortest path from 1 to 6: [1, 4, 7, 6]
Length of the shortest path: 11
RESULT: Thus the python program to find the shortest paths to other vertices using Dijkstra’s
algorithm was executed and verified successfully.

VIVA QUESTIONS

1.What is Dijkstra’s algorithm, and what is its primary use?


2.How does Dijkstra’s algorithm handle graphs with negative edge weights?
3.What data structures are commonly used in the implementation of Dijkstra’s algorithm?
4.What is the time complexity of Dijkstra’s algorithm?
5.What are the limitations of Dijkstra’s algorithm?
EX.NO:8 IMPLEMENTATION OF PRIM’S ALGORITHM
DATE:

AIM:
To Find the minimum cost spanning tree of a given undirected graph using Prim’s algorithm.
ALGORITHM :

Step 1: Determine the arbitrary starting vertex.


Step 2: Keep repeating steps 3 and 4 until the fringe vertices (vertices not included in MST) remain.
Step 3: Select an edge connecting the tree vertex and fringe vertex having the minimum weight.
Step 4: Add the chosen edge to MST if it doesn’t form any closed cycle.
Step 5: Exit

PROGRAM :
import matplotlib.pyplot as plt
import networkx as nx
import pylab

# Create an empty Undirected Weighted Graph


G = nx.Graph()

# Add nodes
nodes_list = [1, 2, 3, 4, 5, 6, 7]
G.add_nodes_from(nodes_list)

# Add weighted edges


edges_list = [(1, 2, 1), (1, 4, 4), (2, 3, 2), (2, 4, 6), (2, 5, 4), (3, 5, 5),
(3, 6, 6), (4, 5, 3), (4, 7, 4), (5, 6, 8), (5, 7, 7), (6, 7, 3)]
G.add_weighted_edges_from(edges_list)

pos=nx.spring_layout(G)
pylab.figure(1)
nx.draw(G,pos, with_labels= 'true')
# use default edge labels
nx.draw_networkx_edge_labels(G,pos)

# Calculate a minimum spanning tree of an undirected weighted graph with


# the Prim algorithm
mst = nx.minimum_spanning_tree(G, algorithm='prim')
print(sorted(mst.edges(data=True)))
OUTPUT:
[(1, 2, {'weight': 1}), (1, 4, {'weight': 4}), (2, 3, {'weight': 2}), (4, 5, {'weight': 3}), (4, 7, {'weight':
4}), (6, 7, {'weight': 3})]

RESULT: Thus the python program for implementation of minimum cost spanning tree of a given
undirected graph using Prim’s algorithm.

VIVA QUESTIONS
1.What is Prim's algorithm?
2.What is the time complexity of Prim's algorithm?
3.What are the differences between Prim's and Kruskal's algorithms for finding an MST?
4.What are the practical applications of Minimum Cost Spanning Tree algorithms like Prim's?
5.What are the advantages and disadvantages of using Prim’s algorithm?

EX.NO:9 IMPLEMENTATION OF FLOYD’S ALGORITHM FOR THE ALL-PAIRS-


SHORTEST- PATHS PROBLEM
DATE:

AIM: To Implement Floyd’s algorithm for the All-Pairs- Shortest-Paths problem.


ALGORITHM:
Step1: In this program, INF represents infinity, and the floyd_algorithm function takes in a weighted
graph represented as a two-dimensional list where graph[i][j] is the weight of the edge from vertex i to
vertex j.
Step:2 The function returns a two-dimensional list dist where dist[i][j] is the shortest path from vertex i
to vertex j.
Step:3 The algorithm first initializes the dist list with the weights of the edges in the graph. It then uses
three nested loops to find the shortest path from vertex i to vertex j through vertex k.
Step:4 If the path through k is shorter than the current shortest path from i to j, it updates dist[i][j] with
the new shortest path.
Step:5 Finally, the program calls the floyd_algorithm function on a sample input graph and prints the
resulting dist list.

PROGRAM:
INF = float('inf')

def floyd_algorithm(graph):
n = len(graph)
dist = [[INF for j in range(n)] for i in range(n)]

for i in range(n):
for j in range(n):
if graph[i][j] != 0:
dist[i][j] = graph[i][j]

for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j]

return dist

# Sample input
graph = [
[0, 5, INF, 10],
[INF, 0, 3, INF],
[INF, INF, 0, 1],
[INF, INF, INF, 0]
]

# Run the algorithm and print the result


result = floyd_algorithm(graph)
for row in result:
print(row)

OUTPUT:
[inf, 5, 8, 9]
[inf, inf, 3, 4]
[inf, inf, inf, 1]
[inf, inf, inf, inf]
RESULT: Thus the python program for implementation of Floyd’s algorithm for the All-Pairs-
Shortest-Paths problem was executed and verified successfully.

VIVA QUESTIONS

1.What is Floyd’s algorithm, and how does it solve the All-Pairs-Shortest-Paths problem?
2.What is the time complexity of Floyd’s algorithm?
3.How does Floyd’s algorithm handle graphs with negative edge weights?
4.What is the space complexity of Floyd’s algorithm, and how is the data stored during the execution?
5.What are the advantages and disadvantages of Floyd’s algorithm?

EX.NO:10 COMPUTE THE TRANSITIVE CLOSURE OF A DIRECTED GRAPH USING


WARSHALL'S ALGORITHM
DATE:

AIM: To Compute the transitive closure of a given directed graph using Warshall's algorithm.

ALGORITHM:
Step1: In this program, graph is a two-dimensional list representing the directed graph where graph[i][j]
is 1 if there is an edge from vertex i to vertex j, and 0 otherwise.
Step2: The warshall_algorithm function returns a two-dimensional list representing the transitive
closure of the input graph.
Step3: The algorithm first creates a copy of the input graph as the initial transitive closure. It then uses
three nested loops to update the transitive closure by checking if there is a path from vertex i to vertex j
through vertex k. If there is, it sets transitive_closure[i][j] to 1.
Step4: Finally, the program calls the warshall_algorithm function on a sample input graph and prints the
resulting transitive closure.

PROGRAM:
def warshall_algorithm(graph):
n = len(graph)

# Create a copy of the original graph


transitive_closure = [row[:] for row in graph]

# Compute the transitive closure using Warshall's algorithm


for k in range(n):
for i in range(n):
for j in range(n):
transitive_closure[i][j] = transitive_closure[i][j] or (transitive_closure[i][k] and transitive_clos
ure[k][j])

return transitive_closure

# Sample input
graph = [
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
[1, 0, 0, 0]
]

# Run the algorithm and print the result


result = warshall_algorithm(graph)
for row in result:
print(row)

OUTPUT:
[1, 1, 1, 1]
[1, 1, 1, 1]
[1, 1, 1, 1]
[1, 1, 1, 1]
RESULT: Thus the python program to Compute the transitive closure of a given directed graph using
Warshall's algorithm was executed and verified successfully.

VIVA QUESTIONS

1.What is Warshall’s algorithm?


2.What is the time complexity of Warshall’s algorithm, and how does it depend on the number of
vertices in the graph?
3.How is the transitive closure of a graph represented, and what does the result of Warshall’s algorithm
look like?
4.What are the conditions for two vertices to be reachable from one another in a directed graph,
according to Warshall’s algorithm?
5.What is the difference between Warshall's algorithm for computing transitive closure and the Floyd-
Warshall algorithm for shortest paths?
EX.NO:11 IMPLEMENTATION OF FINDING THE MAXIMUM AND MINIMUM
NUMBERS IN A IST USING DIVIDE AND CONQUER TECHNIQUE
DATE:

AIM: To Develop a program to find out the maximum and minimum numbers in a given list of n
numbers using the divide and conquer technique.
ALGORITHM:
Step1: The find_max_min function recursively divides the list into two halves until the base
cases are reached (when the list contains only one or two elements).
Step2: In the base case, the maximum and minimum numbers are returned.
Step3: In the recursive case, the maximum and minimum numbers of the left and right halves
are computed and the maximum and minimum of the whole list is returned using the max and
min functions.
PROGRAM:
def find_max_min(arr):
if len(arr) == 1:
return arr[0], arr[0]
elif len(arr) == 2:
if arr[0] > arr[1]:
return arr[0], arr[1]
else:
return arr[1], arr[0]
else:
mid = len(arr) // 2
left_max, left_min = find_max_min(arr[:mid])
right_max, right_min = find_max_min(arr[mid:])
return max(left_max, right_max), min(left_min, right_min)
# Example usage
arr = [3, 1, 5, 2, 9, 7]
max_num, min_num = find_max_min(arr)
print("Maximum number:", max_num)
print("Minimum number:", min_num)

OUTPUT:
Maximum number: 9
Minimum number: 1
RESULT: Thus the python program for find out the maximum and minimum numbers in a
given list of n numbers using the divide and conquer technique was executed and verified
successfully.

VIVA QUESTIONS
1.What is the divide and conquer approach?
2.How does the recursive divide and conquer approach work to find both the maximum and minimum
values simultaneously?
3.What is the time complexity of the divide and conquer approach?
4.How does the number of comparisons in the divide and conquer approach compare to a simple linear
scan?
5.What are the advantages of using the divide and conquer method over other approaches to find the
maximum and minimum values in a list?

EX.NO:12(A) IMPLEMENTATION OF MERGE SORT


DATE:

AIM: To Implement Merge sort method to sort an array of elements and determine the time
required to sort. Repeat the experiment for different values of n, the number of elements in the list
to be sorted and plot a graph of the time taken versus n.
ALGORITHM:
Step1: The program first defines the merge_sort() function which implements the Merge sort
algorithm.
Step2: It then defines a test_merge_sort() function which generates a list of n random numbers,
sorts the list using Merge sort, and measures the time required to sort the list.
Step3: Finally, the program tests the test_merge_sort() function for different values of n and
plots a graph of the time taken versus n using the Matplotlib library.
PROGRAM:
import random
import time
import matplotlib.pyplot as plt
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]

merge_sort(left_half)
merge_sort(right_half)
i=j=k=0
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1

while i < len(left_half):


arr[k] = left_half[i]
i += 1
k += 1

while j < len(right_half):


arr[k] = right_half[j]
j += 1
k += 1

def test_merge_sort(n):
arr = [random.randint(1, 100) for _ in range(n)]
start_time = time.time()
merge_sort(arr)
end_time = time.time()
return end_time - start_time

if __name__ == '__main__':
ns = [10, 100, 1000, 10000, 100000]
times = []
for n in ns:
t = test_merge_sort(n)
times.append(t)
print(f"Merge sort took {t:.6f} seconds to sort {n} elements.")

plt.plot(ns, times, 'o-')


plt.xlabel('Number of elements (n)')
plt.ylabel('Time taken (s)')
plt.title('Merge Sort')
plt.show()
OUTPUT:
Merge sort took 0.000020 seconds to sort 10 elements.
Merge sort took 0.000249 seconds to sort 100 elements.
Merge sort took 0.003046 seconds to sort 1000 elements.
Merge sort took 0.021679 seconds to sort 10000 elements.
Merge sort took 0.283631 seconds to sort 100000 elements.
RESULT: Thus the python program for Implementation of Merge sort method to sort an array of
elements and determine the time required to sort. Repeat the experiment for different values of n,
the number of elements in the list to be sorted and plot a graph of the time taken versus n was
executed and verified successfully.

VIVA QUESTIONS
1.What is Merge Sort, and how does it work to sort an array?
2.What is the time complexity of Merge Sort, and how is it derived?
3.What is the space complexity of Merge Sort, and why does it require additional space?
4.How does Merge Sort compare to other sorting algorithms like Quick Sort or Insertion Sort in terms of
performance?
5.What are the advantages and disadvantages of using Merge Sort in real-world applications?

EX.NO:12(B) IMPLEMENTATION OF QUICK SORT


DATE:
AIM: To Implement Quick sort method to sort an array of elements and determine the time
required to sort. Repeat the experiment for different values of n, the number of elements in the list
to be sorted and plot a graph of the time taken versus n.
ALGORITHM:
Step1: This program generates a list of random integers of size n, sorts the list using the quicksort
function, and measures the time required to sort the list.
Step2: It repeats this process num_repeats times and returns the average time taken.
Step3: The main function of the program tests the measure_time function for different values of n and
plots a graph of the time taken versus n.
Step4: The maximum value of n is set to max_n, and the step size between values of n is set to
step_size.
Step5: The program uses the built-in random and time modules to generate random integers and
measure time, respectively. Additionally, the quicksort function is implemented recursively and sorts
the list in ascending order.
PROGRAM:
import random
import time
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
left = []
right = []
for i in range(1, len(arr)):
if arr[i] < pivot:
left.append(arr[i])
else:
right.append(arr[i])
return quicksort(left) + [pivot] + quicksort(right)
def measure_time(n, num_repeats):
times = []
for _ in range(num_repeats):
arr = [random.randint(0, 1000000) for _ in range(n)]
start_time = time.time()
quicksort(arr)
end_time = time.time()
times.append(end_time - start_time)
return sum(times) / len(times)
if __name__ == '__main__':
num_repeats = 10
max_n = 10000
step_size = 100
ns = range(0, max_n + step_size, step_size)
times = []
for n in ns:
if n == 0:
times.append(0)
else:
times.append(measure_time(n, num_repeats))
print(times)

OUTPUT:
[0, 0.00013625621795654297, 0.0006334543228149414, 0.000517892837524414,
0.0009247779846191407, 0.000916147232055664, 0.0010011672973632812,]
RESULT: Thus the implementation of Quick sort method to sort an array of elements and
determine the time required to sort. Repeat the experiment for different values of n, the number of
elements in the list to be sorted and plot a graph of the time taken versus n was executed and
verified successfully.

VIVA QUESTIONS

1.What is the basic working principle of the Quick Sort algorithm?


2.How do you choose a pivot in Quick Sort, and why is it important?
3.What is the time complexity of Quick Sort in the best, average, and worst cases?
4.How do you measure the time required to sort the array in your implementation?
5.What are the advantages and disadvantages of Quick Sort compared to other sorting algorithms
like Merge Sort?

EX.NO:13 IMPLEMENTATION OF N QUEENS PROBLEM USING BACKTRACKING


DATE:

AIM: To Implement N Queens problem using Backtracking.


ALGORITHM:
Step1: The is_safe function checks whether a queen can be placed in the current cell without conflicting
with any other queens on the board.
Step2: The solve_n_queens function places queens one by one in each column, starting from the
leftmost column. If all queens are placed successfully, it returns True. Otherwise, it backtracks and
removes the queen from the current cell and tries to place it in a different row in the same column.
Step3: The print_board function prints the final board configuration after all queens have been placed.
Step4: The n_queens function initializes the board and calls the solve_n_queens function to solve the
N Queens problem. If a solution exists, it prints the board configuration. Otherwise, it prints a message
indicating that a solution does not exist.

PROGRAM:
def is_safe(board, row, col, n):
# Check if there is any queen in the same row
for i in range(col):
if board[row][i] == 1:
return False
# Check upper diagonal on left side
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False
# Check lower diagonal on left side
for i, j in zip(range(row, n), range(col, -1, -1)):
if board[i][j] == 1:
return False
return True

def solve_n_queens(board, col, n):


if col == n:
# All queens have been placed successfully
return True
for row in range(n):
if is_safe(board, row, col, n):
# Place the queen in the current cell
board[row][col] = 1
# Recur to place rest of the queens
if solve_n_queens(board, col + 1, n):
return True
# Backtrack and remove the queen from the current cell
board[row][col] = 0
return False

def print_board(board, n):


for i in range(n):
for j in range(n):
print(board[i][j], end=" ")
print()

def n_queens(n):
# Initialize the board
board = [[0 for j in range(n)] for i in range(n)]
if not solve_n_queens(board, 0, n):
print("Solution does not exist.")
return False
print("Solution:")
print_board(board, n)
return True

if __name__ == "__main__":
n = int(input("Enter the number of queens: "))
n_queens(n)

OUTPUT:
Enter the number of queens: 4
Solution:
0010
1000
0001
0100
RESULT: Thus the python program for Implementation of N Queens problem using Backtracking
Technique was executed and verified successfully.

VIVA QUESTIONS
1.What is the N-Queens problem?

2.What is the time complexity of solving the N-Queens problem using backtracking?

3.How does the solution space grow as the value of N increases?

4.Why is the N-Queens problem classified as a backtracking problem?


5.What are the advantages and disadvantages of using backtracking for the N-Queens problem?

EX.NO:14 IMPLEMENTATION OF ANY SCHEME TO FIND THE OPTIMAL


SOLUTION FOR THE TRAVELING SALESPERSON PROBLEM
DATE:

AIM: To Implement any scheme to find the optimal solution for the Traveling Salesperson
problem and then solve the same problem instance using any approximation algorithm and
determine the error in the approximation.
ALGORITHM:
The following steps involved in solving TSP using branch and bound:
1. Construct a complete graph with the given cities as vertices, where the weight of each edge is the
distance between the two cities.
2. Initialize the lower bound to infinity and create an empty path.
3. Choose a starting vertex and add it to the path.
4. For each remaining vertex, compute the lower bound for the path that includes this vertex and add it
to the priority queue.
5. While the priority queue is not empty, select the path with the lowest lower bound and extend it by
adding the next vertex.
6. Update the lower bound for the new path and add it to the priority queue.
7. If all vertices have been added to the path, update the lower bound to the length of the complete tour
and update the optimal tour if the new tour is shorter.
8. Backtrack to the previous vertex and explore other paths until all paths have been explored.

PROGRAM:
import itertools
import math
import time
# Function to calculate the distance between two cities
def distance(city1, city2):
return math.sqrt((city1[0] - city2[0])**2 + (city1[1] - city2[1])**2)
# Function to find the optimal solution using brute force
def tsp_brute_force(cities):
# Calculate all possible permutations of the cities
permutations = itertools.permutations(cities)

# Initialize the shortest path to infinity


shortest_path = float('inf')

# Iterate over all permutations to find the shortest path


for permutation in permutations:
path_length = 0
for i in range(len(permutation)-1):
path_length += distance(permutation[i], permutation[i+1])
path_length += distance(permutation[-1], permutation[0])

# Update the shortest path if the current path is shorter


if path_length < shortest_path:
shortest_path = path_length
shortest_path_order = permutation

return shortest_path, shortest_path_order

# Function to find the approximate solution using the nearest neighbor algorithm
def tsp_nearest_neighbor(cities):
# Start with the first city in the list as the current city
current_city = cities[0]
visited_cities = [current_city]

# Iterate over all cities to find the nearest neighbor


while len(visited_cities) < len(cities):
nearest_neighbor = None
nearest_distance = float('inf')
for city in cities:
if city not in visited_cities:
distance_to_city = distance(current_city, city)
if distance_to_city < nearest_distance:
nearest_distance = distance_to_city
nearest_neighbor = city
# Add the nearest neighbor to the visited cities
visited_cities.append(nearest_neighbor)
current_city = nearest_neighbor

# Calculate the total distance of the path


total_distance = 0
for i in range(len(visited_cities)-1):
total_distance += distance(visited_cities[i], visited_cities[i+1])
total_distance += distance(visited_cities[-1], visited_cities[0])

return total_distance, visited_cities

# Generate a list of random cities


cities = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)]

# Find the optimal solution using brute force


start_time = time.time()
optimal_path_length, optimal_path_order = tsp_brute_force(cities)
end_time = time.time()
print("Optimal path length:", optimal_path_length)
print("Optimal path order:", optimal_path_order)
print("Time taken (brute force):", end_time - start_time, "seconds")

# Find the approximate solution using the nearest neighbor algorithm


start_time = time.time()
approximate_path_length, approximate_path_order = tsp_nearest_neighbor(cities)
end_time = time.time()
OUTPUT:
Optimal path length: 25.455844122715707
Optimal path order: ((0, 0), (1, 1), (2, 2), (4, 4), (5, 5), (8, 8), (9, 9), (7, 7), (6, 6), (3, 3))
Time taken (brute force): 45.78943109512329 seconds

RESULT: Thus the python program for implementation of any scheme to find the optimal
solution for the Traveling Salesperson problem and then solve the same problem instance using
any approximation algorithm and determine the error in the approximation was executed and
verified successfully.

VIVA QUESTIONS

1.What is the Traveling Salesperson Problem (TSP)? Why is it considered computationally hard?
2.How did you calculate the error in the approximation solution?
3.What are the trade-offs between exact and approximation algorithms for solving the TSP?
4.What is a greedy algorithm, and how is it used in TSP?
5.What is the time complexity of solving TSP?
EX.NO:15 IMPLEMENTATION OF RANDOMIZED ALGORITHMS FOR FINDING THE
KTH SMALLEST NUMBER
DATE:
AIM: To Implement randomized algorithms for finding the kth smallest number.
ALGORITHM:
1. The partition() function takes an array arr, low index low, and high index high as input and
partitions the array around a randomly chosen pivot. It returns the index of the pivot element.
2.The randomized_select() function takes an array arr, low index low, high index high, and the value
of k as input and returns the kth smallest element in the array. It first selects a random pivot element
using random.randint() function and partitions the array using the partition() function. Then it
recursively calls itself on either the left or right partition depending on the position of the pivot element.
3.In the main section, we define an array arr and the value of k. Then we calculate the length of the
array n and call the randomized_select() function on the array to find the kth smallest element.

PROGRAM:
import random
# Function to partition the array around a pivot
def partition(arr, low, high):
i = low - 1
pivot = arr[high]
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return i+1

# Function to find the kth smallest number using randomized algorithm


def randomized_select(arr, low, high, k):
if low == high:
return arr[low]
pivot_index = random.randint(low, high)
arr[pivot_index], arr[high] = arr[high], arr[pivot_index]
index = partition(arr, low, high)
if k == index:
return arr[k]
elif k < index:
return randomized_select(arr, low, index-1, k)
else:
return randomized_select(arr, index+1, high, k)

# Testing the function


arr = [9, 4, 2, 7, 3, 6]
k=3
n = len(arr)
result = randomized_select(arr, 0, n-1, k-1)
print(f"The {k}th smallest number is: {result}")
OUTPUT:
The 3th smallest number is: 4

RESULT: Thus the python program for implementation of randomized algorithms for finding the k th
smallest number was executed and verified successfully.

VIVA QUESTIONS
1.What is a randomized algorithm, and why is it used to find the kth smallest number?
2.What is the expected time complexity of the randomized algorithm for finding the kth smallest
number?
3.How does the Randomized Quickselect algorithm work?
4.What are the key differences between deterministic and randomized approaches for the kth smallest
problem?
5.What are the potential drawbacks of using a randomized algorithm in this context?
CONTENT
BEYOND
SYLLABUS
EX. NO: 1 Knuth-Morris-Pratt (KMP) algorithm
DATE:
AIM:
To implement the Knuth-Morris-Pratt (KMP) algorithm.

Algorithm:
1. First, create a prefix array to store the longest proper prefix of the pattern that is also a
suffix of its first i characters.
2. Initialize two pointers, i and j, to 0, which will track the current indices of the text and
pattern, respectively.
3. While i is less than the length of the text, do the following: a. If the character at index
i in the text matches the character at index j in the pattern, increment both i and j. b. If
j is equal to the length of the pattern, then a match has been found at index i-j. Store
this index and continue searching for other matches. c. If the character at index i in the
text does not match the character at index j in the pattern and j is not 0, update j to be
the value of the prefix array at index j-1 and continue comparing. d. If the character at
index i in the text does not match the character at index j in the pattern and j is 0,
increment i and continue searching.
PROGRAM :
def
compute_lps(pattern):
lps = [0] * len(pattern)
j=0
for i in range(1, len(pattern)):
while j > 0 and pattern[j] !=
pattern[i]: j = lps[j-1]
if pattern[j] ==
pattern[i]: j += 1
lps[i] = j
return lps

def kmp_search(text, pattern):


lps = compute_lps(pattern)
i=0
j=0
while i < len(text):
if text[i] == pattern[j]:
i += 1
j += 1
if j ==
len(pattern):
return i - j
elif j > 0:
j = lps[j-1]
else:
i += 1
return -1
text = "ababcababaad"
pattern = "ababa"
result = kmp_search(text, pattern)
if result != -1:
print(f"Pattern found at index {result}")
else:
print("Pattern not found")
Output:

Pattern found at index 5

Result:
Thus, the program has been executed successfully and the output has been verified.

VIVA QUESTIONS
1.What is the purpose of the Knuth-Morris-Pratt (KMP) algorithm?
2.What is the time complexity of the KMP algorithm?
3.How does the KMP algorithm handle mismatches?
4.What are some real-world applications of the KMP algorithm?
5.What are the limitations of the KMP algorithm?
EX. NO: 2 Rabin Karp algorithm
DATE:

AIM:
To implement the Rabin Karp algorithm.
ALGORITHM:
1. Calculate the hash value of the pattern to be searched.
2. Calculate the hash value of the first substring of the same length in the text.
3. Compare the hash values of the pattern and the substring. If they are equal, compare
the pattern and the substring character by character.
4. If the pattern and the substring match, return the index of the substring in the text.
5. If the pattern and the substring do not match, calculate the hash value of the next
substring of the same length in the text.
6. Repeat steps 3-5 until the end of the text is reached or a match is found.
PROGRAM :
d = 10
def search(pattern, text, q):
m = len(pattern)
n = len(text)
p=0
t=0
h=1
i=0
j=0
for i in range(m-1):
h = (h*d) % q
# Calculate hash value for pattern and text
for i in range(m):
p = (d*p + ord(pattern[i])) %
q t = (d*t + ord(text[i])) % q
# Find the match
for i in range(n-m+1):
if p == t:
for j in range(m):
if text[i+j] != pattern[j]:
break
j += 1
if j == m:
print("Pattern is found at position: " + str(i+1))
if i < n-m:
t = (d*(t-ord(text[i])*h) + ord(text[i+m])) % q
if t < 0:
t = t+q
text = "ABCCDDAEFG"
pattern = "CDD"
q = 13
search(pattern, text, q)
Output:
Pattern is found at position: 4

Result:
Thus, the program has been executed successfully and the output has been verified.
VIVA QUESTIONS

1.What is the purpose of the Rabin-Karp algorithm?


2.How does the Rabin-Karp algorithm calculate hash values?
3.What is the time complexity of the Rabin-Karp algorithm?
4.What are the advantages and disadvantages of the Rabin-Karp algorithm?
5.What are some real-world applications of the Rabin-Karp algorithm?

EX. NO: 3 From a given vertex in a weighted connected graph, develop a program to
find the shortest paths to other vertices using Bellman Ford algorithm.
DATE:

AIM:

To develop a program to find the shortest paths to other vertices using Bellman Ford
algorithm.

ALGORITHM:
1. Initialize the distance of the source vertex to itself as 0, and the distance of all other
vertices to infinity.
2. Repeat the following steps for V-1 times, where V is the number of vertices in the graph:
a. For each edge (u, v) with weight w, update the distance of v as min(distance[v],
distance[u]+w).
3. After V-1 iterations, if there is any improvement in the distances, then the graph
contains a negative-weight cycle.
4. If there is no negative-weight cycle, then the distances obtained after V-1 iterations
are the shortest paths from the source vertex to all other vertices.
PROGRAM:

def bellman_ford(graph, source):


distances = {v: float('inf') for v in graph}
distances[source] = 0
for i in range(len(graph)-1):
for u in graph:
for v, w in graph[u].items():
if distances[u] != float('inf') and distances[u]+w < distances[v]:
distances[v] = distances[u]+w
for u in graph:
for v, w in graph[u].items():
if distances[u] != float('inf') and distances[u]+w < distances[v]:
print("Graph contains negative-weight cycle")
return
print("Shortest paths from source vertex", source)
for v in graph:
print(v, distances[v])

graph = {
'A': {'B': 6, 'D': 1},
'B': {'C': 5},
'C': {'D': 5},
'D': {},
}

bellman_ford(graph, 'A')
OUTPUT:

Shortest paths from source vertex A


A0
B6
C 11
D1

Result:
Thus, the program has been executed successfully and the output has been verified.
VIVA QUESTIONS

1.What is the Bellman-Ford algorithm used for?


2.How does the Bellman-Ford algorithm handle negative weight edges?
3.What is the time complexity of the Bellman-Ford algorithm?
4.What are some practical applications of the Bellman-Ford algorithm?
5.How does the Bellman-Ford algorithm detect negative weight cycles?
EX. NO: 5 Find the minimum cost spanning tree of a given undirected graph
using Kruskal’salgorithm.
DATE:

AIM:
To find the minimum cost spanning tree of a given undirected graph using Kruskal’s
algorithm.

ALGORITHM:

1. Create a priority queue to store all the edges in the graph sorted by their weights.
2. Create a set to keep track of the visited vertices.
3. Initialize a variable 'MST' to an empty list to store the minimum cost spanning tree.
4. Repeat until MST has V-1 edges, where V is the total number of vertices in the graph:
a. Remove the smallest weight edge from the priority queue. b. Check if the edge
connects two vertices that are not in the same set, i.e., there is no cycle formed. c. If
there is no cycle, add the edge to the MST and merge the two sets.
5. Return MST.
PROGRAM :

class Graph:

def init (self,


vertices): self.V =
vertices self.graph =
[]

def addEdge(self, u, v, w):


self.graph.append([u, v, w])

def find(self, parent, i):


if parent[i] != i:
parent[i] = self.find(parent, parent[i])
return parent[i]

def union(self, parent, rank, x, y):


if rank[x] < rank[y]:
parent[x] = y
elif rank[x] > rank[y]:
parent[y] = x
else:
parent[y] = x
rank[x] += 1

def KruskalMST(self):
result = []
i=0
e=0
self.graph = sorted(self.graph,key=lambda item: item[2])
parent = []
rank = []
for node in range(self.V):
parent.append(node)
rank.append(0)
while e < self.V - 1:
u, v, w = self.graph[i]
i=i+1
x = self.find(parent, u)
y = self.find(parent, v)
if x != y:
e=e+1
result.append([u, v, w])
self.union(parent, rank, x, y)
minimumCost = 0
print("Edges in the constructed MST")
for u, v, weight in result:
minimumCost += weight
print("%d -- %d == %d" % (u, v, weight))
print("Minimum Spanning Tree", minimumCost)
if name == ' main ':
g = Graph(4)
g.addEdge(0, 1, 10)
g.addEdge(0, 2, 6)
g.addEdge(0, 3, 5)
g.addEdge(1, 3, 15)
g.addEdge(2, 3, 4)

g.KruskalMST()
Output:

Edges in the constructed MST


2 -- 3 == 4
0 -- 3 == 5
0 -- 1 == 10
Minimum Spanning Tree 19

Result:
Thus, the program has been executed successfully and the output has been verified.

VIVA QUESTIONS

1.What is Kruskal’s algorithm used for?


2.What is the time complexity of Kruskal’s algorithm?
3.What is the difference between Kruskal’s and Prim’s algorithms?
4.What are the limitations of Kruskal’s algorithm?
5.What data structure is used to detect cycles in Kruskal’s algorithm?
EX. NO: 5 Hamiltonian Circuit
DATE:

AIM:
To determine whether a Hamiltonian Circuit exists in a given graph and, if so, to find such a circuit
using a backtracking approach.
ALGORITHM:
Step 1: Start with a graph GGG with nnn vertices.
Step 2: Initialize a path array path[]path[]path[] of size n+1n+1n+1, with the first vertex as the
starting point.
Step 3: Use a recursive function to try adding vertices to the current path:
a. Check if the vertex is adjacent to the last vertex in the path.
b. Ensure the vertex is not already in the path.
Step 4: If all vertices are in the path and the last vertex connects back to the first, a
Hamiltonian Circuit is found.
Step 5: If no vertex can be added without violating the constraints, backtrack and try another
vertex.
Step 6: If the path array forms a circuit, print it.

PROGRAM :

def is_safe(v, graph, path, pos):

# Check if the current vertex is adjacent to the previous vertex in the path

if graph[path[pos - 1]][v] == 0:

return False

# Check if the vertex is already in the path

if v in path:

return False
return True

def hamiltonian_cycle_util(graph, path, pos):

# Base case: If all vertices are in the path

if pos == len(graph):

# Check if there is an edge from the last vertex to the first

if graph[path[pos - 1]][path[0]] == 1:

return True

else:

return False

# Try different vertices as the next candidate

for v in range(1, len(graph)):

if is_safe(v, graph, path, pos):

path[pos] = v

if hamiltonian_cycle_util(graph, path, pos + 1):

return True

# Backtrack

path[pos] = -1

return False
def hamiltonian_cycle(graph):

n = len(graph)

path = [-1] * n

path[0] = 0 # Start from the first vertex

if not hamiltonian_cycle_util(graph, path, 1):

return "No Hamiltonian Circuit exists"

else:

path.append(path[0]) # Add the starting vertex to complete the cycle

return path

# Example usage

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]

result = hamiltonian_cycle(graph)

print("Hamiltonian Circuit:" if isinstance(result, list) else result, result)


Output:

Hamiltonian Circuit: [0, 1, 2, 4, 3, 0]

Result:
Thus, the program has been executed successfully and the output has been verified.

VIVA QUESTIONS

1.What is a Hamiltonian Circuit?


2.What is the time complexity of finding a Hamiltonian Circuit using backtracking?
3.How does the algorithm ensure that no vertex is visited more than once?
4.Can the algorithm handle disconnected graphs? Why or why not?
5.What are some real-world applications of Hamiltonian Circuits?

You might also like