0% found this document useful (0 votes)
8 views36 pages

Algorithms Lab File for B.Tech CSE

This document outlines the lab file for the Design and Analysis of Algorithms course for B.Tech CSE 3rd Year students at KCC Institute of Technology and Management. It includes a list of experiments with corresponding dates and signatures, covering various algorithms such as sorting, searching, and optimization problems. The document also provides code snippets for implementing these algorithms in Python and Java.

Uploaded by

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

Algorithms Lab File for B.Tech CSE

This document outlines the lab file for the Design and Analysis of Algorithms course for B.Tech CSE 3rd Year students at KCC Institute of Technology and Management. It includes a list of experiments with corresponding dates and signatures, covering various algorithms such as sorting, searching, and optimization problems. The document also provides code snippets for implementing these algorithms in Python and Java.

Uploaded by

shubhamyk6369
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

KCCINSTITUTEOFTECHNOLOGYANDMANAGEMENT

2B,2C,VashishthRd,KnowledgeParkIII,GreaterNoida,UttarPradesh201306

DepartmentofComputerScienc
e & Engineering

DESIGN AND ANALYSIS OF


ALGORITHMSLAB FILE
BCS-553

[Link].CSE3rdYear(5thSem)

Academic Session 2025-26

Submitted By: SubmittedTo:


Student Name: Satyam Shukla Dr. Mohd Sadim
Branch: AI&ML (Professor , CSE Deptt.)

Roll Number: 2304921640105


List of Experiments
Exp. Experiment Name Date Signature
No.
1. Program for Recursive Binary & Linear Search. 12/09/2025

2. Program for Heap Sort. 12/09/2025

3. Program for Merge Sort. 19/09/2025

4. Program for Selection Sort. 19/09/2025

5. Program for Insertion Sort. 26/09/2025

6. Program for Quick Sort. 26/09/2025

7. Knapsack Problem using Greedy Solution 10/10/2025

8. Perform Travelling Salesman Problem 10/10/2025

9. Find Minimum Spanning Tree using Kruskal’s 17/10/2025


Algorithm
10. Implement N Queen Problem using Backtracking 17/10/2025

11. Sort a given set of n integer elements using 31/10/2025


Quick Sort method and compute its time
complexity. Run the program for varied values of
n> 5000 and record the time taken to sort. Plot a
graph of the time taken versus non graph sheet.
The elements can be read from a file or can be
generated using the random number generator.
Demonstrate using Java how the divide and-
conquer method works along with its time
complexity analysis: worst case, average case
and best case.
12. Sort a given set of n integer elements using 31/10/2025
Merge Sort method and compute its time
complexity. Run the program for varied values of
n> 5000, and record the time taken to sort. Plot a
graph of the time taken versus non graph sheet.
The elements can be read from a file or can be
generated using the random number generator.
Demonstrate how the divide andconquer method
works along with its time complexity analysis:
worst case, average case and best case.
13. Implement, the 0/1 Knapsack problem using; 07/11/2025
(a). Dynamic Programming method
(b). Greedy method.
14. From a given vertex in a weighted connected 07/11/2025
graph, find shortest paths to other vertices using
Dijkstra's algorithm.
15. Find Minimum Cost Spanning Tree of a given 14/11/2025
connected undirected graph using Kruskal's
algorithm. Use Union-Find algorithms in your
program.
16. Find Minimum Cost Spanning Tree of a given 14/11/2025
undirected graph using Prim’s algorithm.
17. Write programs to; 21/11/2025
(a) Implement All-Pairs Shortest Paths problem
using Floyd's algorithm.
(b) Implement Travelling Sales Person problem
using Dynamic programming.
18. Design and implement to find a subset of a given 21/11/2025
set S = {Sl, S2,. ....,Sn} of n positive integers
whose SUM is equal to a given positive integer
d. For example, if S ={1, 2, 5, 6, 8} and d= 9,
there are two solutions {1,2,6}and {1,8}. Display
a suitable message, if the given problem instance
doesn't have a solution.
19. Design and implement to find all Hamiltonian 28/11/2025
Cycles in a connected undirected Graph G of n
vertices using backtracking principle.
Problem #1
Q. Program for Recursive Binary & Linear Search.
def linear_search(arr, target):
for i in range(len(arr)):
if arr[i] == target:
return i
return -1

def binary_search_recursive(arr, target, low, high):


if high >= low:
mid = (high + low) // 2
if arr[mid] == target:
return mid
elifarr[mid] > target:
return binary_search_recursive(arr, target, low, mid - 1)
else:
return binary_search_recursive(arr, target, mid + 1, high)
else:
return -1

if __name__ == '__main__':
data = [2, 3, 4, 10, 40]
target = 10

linear_result = linear_search(data, target)


print(f"Linear Search - Element {target} found at index: {linear_result}")

data_sorted = sorted(data)
binary_result = binary_search_recursive(data_sorted, target, 0, len(data_sorted) - 1)
print(f"Recursive Binary Search - Element {target} found at index: {binary_result}")

Output:
Problem #2
Q. Program for Heap Sort.
def heapify(arr, n, i):
largest = i
l=2*i+1
r=2*i+2

if l < n and arr[i] <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)

if __name__ == '__main__':
data = [12, 11, 13, 5, 6, 7]
heap_sort(data)
print("Sorted array using Heap Sort is:")
print(data)

Output:
Problem #3
Q. Program for Merge Sort.
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]

merge_sort(L)
merge_sort(R)

i=j=k=0

while i<len(L) and j <len(R):


if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1

while i< len(L):


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

while j <len(R):
arr[k] = R[j]
j += 1
k += 1

if __name__ == '__main__':
data = [38, 27, 43, 3, 9, 82, 10]
merge_sort(data)
print("Sorted array using Merge Sort is:")
print(data)

Output:
Problem #4
Q. Program for Selection Sort.
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if arr[j] <arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]

if __name__ == '__main__':
data = [64, 25, 12, 22, 11]
selection_sort(data)
print("Sorted array using Selection Sort is:")
print(data)

Output:
Problem #5
Q. Program for Insertion Sort.
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j=i-1
while j >= 0 and key <arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key

if __name__ == '__main__':
data = [12, 11, 13, 5, 6]
insertion_sort(data)
print("Sorted array using Insertion Sort is:")
print(data)

Output:
Problem #6
Q. Program for Quick Sort.
def partition(arr, low, high):
i = (low - 1)
pivot = arr[high]

for j in range(low, high):


if arr[j] <= pivot:
i=i+1
arr[i], arr[j] = arr[j], arr[i]

arr[i + 1], arr[high] = arr[high], arr[i + 1]


return (i + 1)

def quick_sort(arr, low, high):


if len(arr) == 1:
return arr
if low < high:
pi = partition(arr, low, high)
quick_sort(arr, low, pi - 1)
quick_sort(arr, pi + 1, high)

if __name__ == '__main__':
data = [10, 7, 8, 9, 1, 5]
n = len(data)
quick_sort(data, 0, n - 1)
print("Sorted array using Quick Sort is:")
print(data)

Output:
Problem #7
Q. Knapsack Problem using Greedy Solution
def knapsack_greedy(W, wt, val):
n = len(wt)
items = []
for i in range(n):
[Link]((val[i] / wt[i], wt[i], val[i]))

[Link](key=lambda x: x[0], reverse=True)

max_value = 0.0
current_weight = 0

for ratio, weight, value in items:


if current_weight + weight <= W:
current_weight += weight
max_value += value
else:
remaining_capacity = W - current_weight
fraction = remaining_capacity / weight
max_value += value * fraction
current_weight += remaining_capacity
break

return max_value

if __name__ == '__main__':
val = [60, 100, 120]
wt = [10, 20, 30]
W = 50
max_val = knapsack_greedy(W, wt, val)
print(f"Maximum value in Knapsack (Greedy): {max_val}")

Output:
Problem #8
Q. Perform Travelling Salesman Problem
import itertools
import sys

def calculate_path_cost(path, graph):


cost = 0
for i in range(len(path) - 1):
cost += graph[path[i]][path[i+1]]
cost += graph[path[-1]][path[0]]
return cost

def travelling_salesman_problem(graph):
n = len(graph)
nodes = list(range(n))
min_cost = [Link]
best_path = []

for path in [Link](nodes):


current_cost = calculate_path_cost(path, graph)
if current_cost<min_cost:
min_cost = current_cost
best_path = path

final_path = list(best_path) + [best_path[0]]


return min_cost, final_path

if __name__ == '__main__':
graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]

min_cost, path = travelling_salesman_problem(graph)


print(f"Minimum cost for TSP (Brute Force): {min_cost}")
print(f"Optimal path (starting and ending at node 0): {path}")
Output:
Problem #9
Q. Find Minimum Spanning Tree using Kruskal’s Algorithm
def kruskals_mst_basic(graph):
edges = []
for u in range(len(graph)):
for v in range(u + 1, len(graph)):
if graph[u][v] != 0:
[Link]((graph[u][v], u, v))
[Link]()

parent = list(range(len(graph)))
def find_set(i):
if parent[i] == i:
return i
return find_set(parent[i])

def union_sets(i, j):


i_id = find_set(i)
j_id = find_set(j)
if i_id != j_id:
parent[i_id] = j_id
return True
return False

mst_weight = 0
mst_edges = []

for weight, u, v in edges:


if union_sets(u, v):
mst_weight += weight
mst_edges.append((u, v, weight))
return mst_weight, mst_edges

if __name__ == '__main__':
graph = [
[0, 2, 0, 6, 0],
[2, 0, 3, 8, 5],
[0, 3, 0, 0, 7],
[6, 8, 0, 0, 9],
[0, 5, 7, 9, 0]
]
weight, edges = kruskals_mst_basic(graph)
print(f"Minimum Spanning Tree Weight (Kruskal's Basic): {weight}")
print("Edges in MST:")
print(edges)
Output:
Problem #10
Q. Implement N Queen Problem using Backtracking
def is_safe(board, row, col, N):
for i in range(col):
if board[row][i] == 1:
return False
i, j = row, col
while i>= 0 and j >= 0:
if board[i][j] == 1:
return False
i -= 1
j -= 1
i, j = row, col
while i< N and j >= 0:
if board[i][j] == 1:
return False
i += 1
j -= 1
return True

def solve_n_queen_util(board, col, N, solutions):


if col >= N:
solution = ["".join(["Q" if board[i][j] == 1 else "." for j in range(N)]) for i in range(N)]
[Link](solution)
return
for i in range(N):
if is_safe(board, i, col, N):
board[i][col] = 1
solve_n_queen_util(board, col + 1, N, solutions)
board[i][col] = 0

def n_queen_problem(N):
board = [[0] * N for _ in range(N)]
solutions = []
solve_n_queen_util(board, 0, N, solutions)
return solutions

if __name__ == '__main__':
N=4
solutions = n_queen_problem(N)
print(f"Total solutions for N={N} Queens: {len(solutions)}")
if solutions:
print("One solution:")
for row in solutions[0]:
print(row)
Output:
Problem #11
Q. Sort a given set of n integer elements using Quick Sort method and compute
its time complexity. Run the program for varied values of n> 5000 and record the
time taken to sort. Plot a graph of the time taken versus non graph sheet. The
elements can be read from a file or can be generated using the random number
generator. Demonstrate using Java how the divide and- conquer method works
along with its time complexity analysis: worst case, average case and best case.
import [Link];
import [Link];
import [Link];

public class QuickSortPerformance {

private void swap(int[] arr, int i, int j) {


int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

private int partition(int[] arr, int low, int high) {


int pivot = arr[high];
int i = (low - 1);

for (int j = low; j < high; j++) {


if (arr[j] <= pivot) {
i++;
swap(arr, i, j);
}
}

swap(arr, i + 1, high);
return (i + 1);
}

public void quickSort(int[] arr, int low, int high) {


if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}

public static void main(String[] args) {


QuickSortPerformance sorter = new QuickSortPerformance();
int[] nValues = {5000, 10000, 20000};
Random rand = new Random();

[Link]("--- Quick Sort Performance Analysis (n > 5000) ---");


[Link]("%-15s | %s%n", "Array Size (n)", "Time Taken (ms)");
[Link]("-------------------------------------");

for (int n :nValues) {


int[] arrRandom = new int[n];
for (int i = 0; i< n; i++) {
arrRandom[i] = [Link](100000);
}

long startTime = [Link]();


[Link](arrRandom, 0, n - 1);
long endTime = [Link]();

long duration = [Link](endTime - startTime);


[Link]("%-15d | %d%n", n, duration);
}

[Link]("\n--- Time Complexity Analysis (Quick Sort) ---");


[Link]("Divide-and-Conquer Method:");
[Link]("Quick Sort is a divide-and-conquer algorithm. It selects an element as a pivot and
partitions the array around the pivot. The sub-arrays are then recursively sorted.");

[Link]("\nWorst Case Time Complexity: O(n^2)");


[Link]("Occurs when the pivot selection consistently leads to the most unbalanced partition
(e.g., array is already sorted or reverse sorted, and the pivot is always the smallest/largest element).");

[Link]("\nAverage Case Time Complexity: O(n log n)");


[Link]("Occurs when the pivot divides the array into roughly equal or near-equal halves. This is
the most common case in practice due to random data distribution.");

[Link]("\nBest Case Time Complexity: O(n log n)");


[Link]("Occurs when the partition process always picks the median element as the pivot, leading
to perfectly balanced sub-arrays.");

[Link]("\nInstructions for Plotting:");


[Link]("Use the recorded time data points (n, Time Taken) to manually plot a graph of Time
Taken vs. n on a graph sheet. This plot should visually demonstrate the near O(n log n) behavior of Quick
Sort in the average case.");
}
}
Output:

Graph:
Problem #12
Q. Sort a given set of n integer elements using Merge Sort method and compute
its time complexity. Run the program for varied values of n> 5000, and record
the time taken to sort. Plot a graph of the time taken versus non graph sheet. The
elements can be read from a file or can be generated using the random number
generator. Demonstrate how the divide andconquer method works along with its
time complexity analysis: worst case, average case and best case.
import random
import time

def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]

merge_sort(L)
merge_sort(R)

i=j=k=0

while i<len(L) and j <len(R):


if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1

while i< len(L):


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

while j <len(R):
arr[k] = R[j]
j += 1
k += 1

def run_performance_test():
n_values = [5000, 10000, 20000]
results = {}
print("--- Merge Sort Performance Analysis (n > 5000) ---")
print("Array Size (n) | Time Taken (seconds)")
print("-------------------------------------")

for n in n_values:
arr_random = [[Link](1, 100000) for _ in range(n)]

start_time = [Link]()
merge_sort(arr_random)
end_time = [Link]()

time_taken = end_time - start_time


results[n] = time_taken
print(f"{n:<14} | {time_taken:.6f}")

print("\n--- Time Complexity Analysis (Merge Sort) ---")


print("Divide-and-Conquer Method:")
print("Merge Sort is a divide-and-conquer algorithm that divides the input array into two halves, calls
itself for the two halves, and then merges the two sorted halves. The merging step is the core operation.")
print("\nWorst Case Time Complexity: O(n log n)")
print("Occurs consistently regardless of the input array's arrangement, as Merge Sort always divides the
array into two halves and takes O(n) time to merge.")
print("\nAverage Case Time Complexity: O(n log n)")
print("Same as the worst case.")
print("\nBest Case Time Complexity: O(n log n)")
print("Same as the worst case, demonstrating its stability and reliable performance irrespective of input
order.")
print("\nInstructions for Plotting:")
print("Use the recorded time data points (n, Time Taken) to manually plot a graph of Time Taken vs. n on
a graph sheet. This plot should visually demonstrate the consistent O(n log n) behavior of Merge Sort.")

if __name__ == '__main__':
run_performance_test()
Output:

Graph:
Problem #13
Q. Implement, the 0/1 Knapsack problem using;
(a). Dynamic Programming method
(b). Greedy method.
def knapsack_dp(W, wt, val, n):
K = [[0 for x in range(W + 1)] for x in range(n + 1)]

for i in range(n + 1):


for w in range(W + 1):
if i == 0 or w == 0:
K[i][w] = 0
elif wt[i-1] <= w:
K[i][w] = max(val[i-1] + K[i-1][w-wt[i-1]], K[i-1][w])
else:
K[i][w] = K[i-1][w]

return K[n][W]

def knapsack_greedy(W, wt, val, n):


items = []
for i in range(n):
[Link]((val[i] / wt[i], wt[i], val[i]))

[Link](key=lambda x: x[0], reverse=True)

max_value = 0.0
current_weight = 0

for ratio, weight, value in items:


if current_weight + weight <= W:
current_weight += weight
max_value += value
else:
remaining_capacity = W - current_weight
fraction = remaining_capacity / weight
max_value += value * fraction
break

return max_value

if __name__ == '__main__':
val = [60, 100, 120]
wt = [10, 20, 30]
W = 50
n = len(val)

dp_result = knapsack_dp(W, wt, val, n)


print(f"Maximum value for 0/1 Knapsack (Dynamic Programming): {dp_result}")

greedy_result = knapsack_greedy(W, wt, val, n)


print(f"Maximum value for Fractional Knapsack (Greedy): {greedy_result}")
print("Note: Greedy approach is optimal for Fractional Knapsack, but not for 0/1 Knapsack.")

Output:
Problem #14
Q. From a given vertex in a weighted connected graph, find shortest paths to
other vertices using Dijkstra's algorithm.
import heapq

def dijkstra(graph, start_node):


num_vertices = len(graph)
distances = {node: float('inf') for node in range(num_vertices)}
distances[start_node] = 0
priority_queue = [(0, start_node)]

while priority_queue:
current_distance, current_node = [Link](priority_queue)

if current_distance> distances[current_node]:
continue

for neighbor, weight in graph[current_node].items():


distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
[Link](priority_queue, (distance, neighbor))

return distances

if __name__ == '__main__':
graph = {
0: {1: 4, 2: 8},
1: {0: 4, 2: 11, 3: 8},
2: {0: 8, 1: 11, 4: 7},
3: {1: 8, 4: 2, 5: 9},
4: {2: 7, 3: 2, 5: 6},
5: {3: 9, 4: 6}
}
start_node = 0
shortest_paths = dijkstra(graph, start_node)
print(f"Shortest paths from node {start_node}:")
print(shortest_paths)
Output:
Problem #15
Q. Find Minimum Cost Spanning Tree of a given connected undirected graph
using Kruskal's algorithm. Use Union-Find algorithms in your program.
class DisjointSet:
def __init__(self, n):
[Link] = list(range(n))

def find(self, i):


if [Link][i] == i:
return i
[Link][i] = [Link]([Link][i])
return [Link][i]

def union(self, i, j):


i_root = [Link](i)
j_root = [Link](j)
if i_root != j_root:
[Link][i_root] = j_root
return True
return False

def kruskals_mst(edges, num_vertices):


[Link]()
ds = DisjointSet(num_vertices)
mst_weight = 0
mst_edges = []

for weight, u, v in edges:


if [Link](u, v):
mst_weight += weight
mst_edges.append((u, v, weight))
return mst_weight, mst_edges

if __name__ == '__main__':
edges = [
(4, 0, 1), (8, 0, 7), (8, 1, 2), (11, 1, 7),
(7, 2, 3), (2, 2, 8), (4, 3, 4), (14, 3, 5),
(9, 4, 5), (10, 5, 6), (1, 6, 7), (2, 6, 8),
(6, 7, 8)
]
num_vertices = 9
weight, mst_edges = kruskals_mst(edges, num_vertices)
print(f"Minimum Spanning Tree Weight (Kruskal's with Union-Find): {weight}")
print("Edges in MST:")
print(mst_edges)
Output:
Problem #16
Q. Find Minimum Cost Spanning Tree of a given undirected graph using Prim’s
algorithm.
import heapq

def prims_mst(graph):
num_vertices = len(graph)

min_heap = [(0, 0)]


in_mst = [False] * num_vertices
mst_weight = 0
mst_edges = []

while min_heap:
weight, u = [Link](min_heap)

if in_mst[u]:
continue

in_mst[u] = True
mst_weight += weight

for v in range(num_vertices):
edge_weight = graph[u][v]
if edge_weight> 0 and not in_mst[v]:
[Link](min_heap, (edge_weight, v))

return mst_weight

if __name__ == '__main__':
graph = [
[0, 2, 0, 6, 0],
[2, 0, 3, 8, 5],
[0, 3, 0, 0, 7],
[6, 8, 0, 0, 9],
[0, 5, 7, 9, 0]
]

weight = prims_mst(graph)
print(f"Minimum Spanning Tree Weight (Prim's Algorithm): {weight}")
Output:
Problem #17
Q. Write programs to;
(a) Implement All-Pairs Shortest Paths problem using Floyd's algorithm.
(b) Implement Travelling Sales Person problem using Dynamic programming.
import sys
import math

INF = [Link]

def floyd_warshall(graph):
V = len(graph)
dist = [[graph[i][j] for j in range(V)] for i in range(V)]

for k in range(V):
for i in range(V):
for j in range(V):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

return dist

def tsp_dp(graph):
V = len(graph)
memo = {}

def solve(mask, pos):


if mask == (1 << V) - 1:
return graph[pos][0]

if (mask, pos) in memo:


return memo[(mask, pos)]

min_cost = INF
for next_node in range(V):
if (mask & (1 <<next_node)) == 0:
new_mask = mask | (1 <<next_node)
cost = graph[pos][next_node] + solve(new_mask, next_node)
min_cost = min(min_cost, cost)

memo[(mask, pos)] = min_cost


return min_cost

return solve(1, 0)

if __name__ == '__main__':
floyd_graph = [
[0, 5, INF, 10],
[INF, 0, 3, INF],
[INF, INF, 0, 1],
[INF, INF, INF, 0]
]
all_pairs_shortest_paths = floyd_warshall(floyd_graph)
print("All-Pairs Shortest Paths (Floyd's Algorithm):")
for row in all_pairs_shortest_paths:
print([x if x != INF else "INF" for x in row])

tsp_graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]
min_tsp_cost = tsp_dp(tsp_graph)
print(f"\nMinimum cost for TSP (Dynamic Programming): {min_tsp_cost}")

Output:
Problem #18
Q. Design and implement to find a subset of a given set S = {Sl, S2,. ....,Sn} of n
positive integers whose SUM is equal to a given positive integer d. For example,
if S ={1, 2, 5, 6, 8} and d= 9, there are two solutions {1,2,6}and {1,8}. Display a
suitable message, if the given problem instance doesn't have a solution.
def subset_sum_util(S, d, index, current_subset, solutions):
if d == 0:
[Link](list(current_subset))
return

if index == len(S) or d < 0:


return

current_subset.append(S[index])
subset_sum_util(S, d - S[index], index + 1, current_subset, solutions)
current_subset.pop()

subset_sum_util(S, d, index + 1, current_subset, solutions)

def subset_sum(S, d):


solutions = []
[Link]()
subset_sum_util(S, d, 0, [], solutions)
return solutions

if __name__ == '__main__':
S = [1, 2, 5, 6, 8]
d=9
results = subset_sum(S, d)

print(f"Given set S = {S} and target sum d = {d}")


if results:
print("Solutions found:")
for solution in results:
print(solution)
else:
print("No subset found that sums to the target.")
Output:
Problem #19
Q. Design and implement to find all Hamiltonian Cycles in a connected
undirected Graph G of n vertices using backtracking principle.
def is_safe(v, graph, path, pos):
if graph[path[pos - 1]][v] == 0:
return False

if v in path[:pos]:
return False
return True

def ham_cycle_util(graph, path, pos, V, cycles):


if pos == V:
if graph[path[pos - 1]][path[0]] == 1:
[Link](path + [path[0]])
return

for v in range(1, V):


if is_safe(v, graph, path, pos):
path[pos] = v
ham_cycle_util(graph, path, pos + 1, V, cycles)
path[pos] = -1

def find_hamiltonian_cycles(graph):
V = len(graph)
path = [-1] * V
path[0] = 0
cycles = []

ham_cycle_util(graph, path, 1, V, cycles)


return cycles

if __name__ == '__main__':
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]
]

cycles = find_hamiltonian_cycles(graph)
print(f"Found {len(cycles)} Hamiltonian Cycles (starting and ending at node 0):")
for cycle in cycles:
print(cycle)
Output:

Common questions

Powered by AI

Kruskal's algorithm builds the MST by sorting all edges and adding them one by one to the growing set of edges, using the Union-Find data structure to prevent cycles. It is efficient for sparse graphs as it focuses on edges. Prim's algorithm, on the other hand, starts from a node and grows the MST by adding the nearest vertex to the tree sequentially, utilizing a priority queue. Prim's is more suited to dense graphs as it considers vertices and directly integrates them into the MST .

Quick Sort and Merge Sort both utilize the divide-and-conquer approach, yet their time complexities differ due to their partitioning strategies. Quick Sort has a worst-case time complexity of O(n^2) when the pivot results in highly unbalanced partitions, such as when the array is already sorted. In average and best cases, Quick Sort performs at O(n log n) as the pivot typically divides the array into roughly equal parts. Merge Sort consistently maintains a time complexity of O(n log n) across all cases because it always divides the array into two equal halves regardless of the input distribution, achieving balanced partitioning .

The greedy method for the 0/1 Knapsack problem is based on selecting items with the highest value-to-weight ratio until the knapsack is full, not guaranteeing an optimal solution due to its local search nature. Its computational cost is lower, generally O(n log n) due to sorting items by ratio. In contrast, the dynamic programming approach ensures an optimal solution by exploring all combinations of items, using a table to store results of subproblems. This method has a higher computational cost of O(nW), where n is the number of items and W is the knapsack capacity. Dynamic programming is more effective than the greedy approach in finding optimal solutions for the 0/1 Knapsack problem .

Binary Search works on the principle of dividing the search interval in half after comparing the target value to the middle element. For this method to function correctly, the array must be sorted. If the array is not sorted, the algorithm cannot reliably determine which half of the array to discard after comparing the target with the middle element, thereby invalidating the search results .

Backtracking in the N Queens problem involves placing a queen in a column and recursively attempting to place queens in subsequent columns while ensuring no queens threaten each other. If placing a queen leads to a conflict, the algorithm backtracks, removing the last placed queen and trying the next possible row in the column. This methodusc explores all potential solutions and ensures that each valid solution has queens placed such that no two queens are in the same row, column, or diagonal .

The Floyd-Warshall algorithm is advantageous for solving the all-pairs shortest path problem within dense graphs due to its simplicity and the O(n^3) time complexity, which is unaffected by edge weights or graph sparsity. It computes paths between all pairs of vertices concurrently. In contrast, Dijkstra's algorithm is more efficient for sparse graphs and finds single-source shortest paths in O(V^2) using an adjacency matrix or improved with a priority queue to O((V + E) log V), but needs to be repeated for each vertex to solve the all-pairs problem, which may not be practical for larger graphs .

Insertion Sort is advantageous in scenarios with small data sets or partially sorted data due to its simple implementation and adaptive nature, which allows it to perform efficiently with an average time complexity of O(n^2). Its ability to work incrementally makes it competitive when the data is limited in size or nearly sorted, where its complexity can approach O(n). Additionally, it is stable and operates in-place, offering memory efficiency. Unlike other more complex algorithms, its overhead is low, making it suitable for real-time systems that require a fast, straightforward sort .

Both dynamic programming and brute force methods for solving the Travelling Salesman Problem (TSP) face significant computational challenges. The brute force method has a factorial time complexity (O(n!)), attempting every possible route to determine the shortest, which is computationally impractical for even moderate-sized graphs due to exponential growth in possibilities. Although dynamic programming, using methods like Held-Karp, reduces complexity to O(n^2 * 2^n), it is still not feasible for large graphs due to its exponential nature and high memory requirements. Scalability remains the primary issue for both methods .

Quick Sort often outperforms other algorithms like Merge Sort in practice due to its in-place sorting mechanism, which leads to better cache performance and memory usage since it requires no additional allocation for arrays. Its average O(n log n) time complexity is efficient under typical conditions, especially with randomized pivots that prevent the worst-case O(n^2) scenario more effectively. Merge Sort requires extra space for the temporary sub-arrays, which can be costly, despite its stable and consistent time complexity of O(n log n) in all cases .

The priority queue in Prim's algorithm efficiently selects the next edge with the minimum weight that expands the growing Minimum Spanning Tree (MST). This structure allows us to retrieve and update the minimum weight edge efficiently as we explore the graph, ensuring that we can construct the MST in optimal time by selecting the least cost edge at every step .

You might also like