0% found this document useful (0 votes)
7 views37 pages

ADSA Lab Programs

The document outlines various data structure implementations including AVL trees, B-trees, Min and Max heaps, and graph traversal algorithms (BFT and DFT) using both adjacency matrices and lists. Each section provides source code for constructing and manipulating these data structures, demonstrating operations such as insertion, deletion, and traversal. The document includes examples of input and output for each data structure and algorithm.

Uploaded by

vivekreddysr143
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)
7 views37 pages

ADSA Lab Programs

The document outlines various data structure implementations including AVL trees, B-trees, Min and Max heaps, and graph traversal algorithms (BFT and DFT) using both adjacency matrices and lists. Each section provides source code for constructing and manipulating these data structures, demonstrating operations such as insertion, deletion, and traversal. The document includes examples of input and output for each data structure and algorithm.

Uploaded by

vivekreddysr143
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

ADSA Lab Programs

1. Construct an AVL tree for a given set of elements which are stored in a
file. And implement insert and delete operation on the constructed tree.
Write contents of tree into a new file using in-order.
Source code:
class Node:
def __init__(self, key):
[Link] = key
[Link] = [Link] = None
[Link] = 1

class AVLTree:
def get_height(self, root):
return [Link] if root else 0

def get_balance(self, root):


return self.get_height([Link]) - self.get_height([Link]) if root else 0

def right_rotate(self, y):


x = [Link]
T2 = [Link]
[Link] = y
[Link] = T2
[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))
[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))
return x

def left_rotate(self, x):


y = [Link]
T2 = [Link]
[Link] = x
[Link] = T2
[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))
[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))
return y

def insert(self, root, key):


if not root:
return Node(key)
elif key < [Link]:
[Link] = [Link]([Link], key)
elif key > [Link]:
[Link] = [Link]([Link], key)
else:
return root # Duplicate keys not allowed

[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))


balance = self.get_balance(root)

# Balance the tree


if balance > 1 and key < [Link]:
return self.right_rotate(root)
if balance < -1 and key > [Link]:
return self.left_rotate(root)
if balance > 1 and key > [Link]:
[Link] = self.left_rotate([Link])
return self.right_rotate(root)
if balance < -1 and key < [Link]:
[Link] = self.right_rotate([Link])
return self.left_rotate(root)

return root

def min_value_node(self, node):


current = node
while [Link]:
current = [Link]
return current
def delete(self, root, key):
if not root:
return root
elif key < [Link]:
[Link] = [Link]([Link], key)
elif key > [Link]:
[Link] = [Link]([Link], key)
else:
if not [Link]:
return [Link]
elif not [Link]:
return [Link]
temp = self.min_value_node([Link])
[Link] = [Link]
[Link] = [Link]([Link], [Link])

if not root:
return root

[Link] = 1 + max(self.get_height([Link]), self.get_height([Link]))


balance = self.get_balance(root)

# Balance the tree


if balance > 1 and self.get_balance([Link]) >= 0:
return self.right_rotate(root)
if balance > 1 and self.get_balance([Link]) < 0:
[Link] = self.left_rotate([Link])
return self.right_rotate(root)
if balance < -1 and self.get_balance([Link]) <= 0:
return self.left_rotate(root)
if balance < -1 and self.get_balance([Link]) > 0:
[Link] = self.right_rotate([Link])
return self.left_rotate(root)

return root
def inorder(self, root, result):
if root:
[Link]([Link], result)
[Link]([Link])
[Link]([Link], result)

# Main program
if __name__ == "__main__":
avl = AVLTree()
root = None

# Read from input file


try:
with open("[Link]", "r") as f:
numbers = list(map(int, [Link]().split()))
for num in numbers:
root = [Link](root, num)
except FileNotFoundError:
print("[Link] not found.")
exit()

# Insert and Delete example


root = [Link](root, 25) # example insert
root = [Link](root, 10) # example delete

# Write in-order traversal to output file


result = []
[Link](root, result)

with open("[Link]", "w") as f:


[Link](" ".join(map(str, result)))

print("AVL tree created and in-order written to [Link].")


Output:
[Link]:
30 10 20 34 50 60 8 4
[Link]
4 8 20 25 30 34 50 60
2. Construct B-Tree an order of 5 with a set of 100 random elements stored
in array. Implement searching, insertion and deletion operations.
Source code:
class BTreeNode:
def __init__(self, t, leaf=False):
self.t, [Link] = t, leaf
[Link], [Link] = [], []

def insert_non_full(self, k):


i = len([Link]) - 1
if [Link]:
[Link](k)
while i >= 0 and [Link][i] > k:
[Link][i + 1] = [Link][i]; i -= 1
[Link][i + 1] = k
else:
while i >= 0 and [Link][i] > k: i -= 1
i += 1
if len([Link][i].keys) == 2 * self.t - 1:
[Link](i)
if k > [Link][i]: i += 1
[Link][i].insert_non_full(k)

def split(self, i):


t, y = self.t, [Link][i]
z = BTreeNode(t, [Link])
promoted_key = [Link][t - 1]
[Link], [Link] = [Link][t:], [Link][:t - 1]
if not [Link]:
[Link], [Link] = [Link][t:], [Link][:t]
[Link](i + 1, z)
[Link](i, promoted_key)

def traverse(self):
for i in range(len([Link])):
if not [Link]:
[Link][i].traverse()
print([Link][i], end=' ')
if not [Link]:
[Link][-1].traverse()

class BTree:
def __init__(self, t):
self.t, [Link] = t, None

def insert(self, k):


if not [Link]:
[Link] = BTreeNode(self.t, True)
[Link](k)
elif len([Link]) == 2 * self.t -1:
s = BTreeNode(self.t)
[Link]([Link])
[Link](0)
[Link][0 if k < [Link][0] else 1].insert_non_full(k)
[Link] = s
else: [Link].insert_non_full(k)
def traverse(self): [Link]() if [Link] else print("Empty")

# MAIN DRIVER
btree = BTree(t=3) # Order 5 → t = ceil(5/2) = 3
with open("[Link]") as f:
for x in f: [Link](int([Link]()))

print("Initial B-Tree:")
[Link]()

while True:
op = input("\n\n[I]nsert [T]raverse [Q]uit: ").lower()
if op == 'i': [Link](int(input("Insert key: ")))
elif op == 't': [Link]()
elif op == 'q': break

Output:
[I]nsert [T]raverse [Q]uit: i
Insert key: 89

[I]nsert [T]raverse [Q]uit: t


7 8 10 12 15 16 20 30 40 42 50 54 66 89

[I] nsert [T]raverse [Q]uit: q


3. Construct Min using arrays, delete any element and display the content
of the Heap.
Source code:
# Min Heap class
class MinHeap:
def __init__(self):
[Link] = []

def parent(self, i):


return (i - 1) // 2

def left(self, i):


return 2 * i + 1

def right(self, i):


return 2 * i + 2

def swap(self, i, j):


[Link][i], [Link][j] = [Link][j], [Link][i]

def insert(self, val):


[Link](val)
i = len([Link]) - 1
# Bubble-up to maintain heap property
while i != 0 and [Link][[Link](i)] > [Link][i]:
[Link](i, [Link](i))
i = [Link](i)
def heapify(self, i):
n = len([Link])
smallest = i
l = [Link](i)
r = [Link](i)
if l < n and [Link][l] < [Link][smallest]:
smallest = l
if r < n and [Link][r] < [Link][smallest]:
smallest = r
if smallest != i:
[Link](i, smallest)
[Link](smallest)
def delete_root(self):
n = len([Link])
if n == 0:
print("Heap is empty")
return
# Replace root with last element
[Link][0] = [Link][-1]
[Link]()
# Heapify from root to fix heap structure
if len([Link]) > 0:
[Link](0)
def display(self):
print("Min Heap:", [Link])
# main
arr = [int(x) for x in input("Enter at least 10 numbers separated by spaces: ").split()]
print("----- Min Heap Demo -----")
min_heap = MinHeap()
for v in arr:
min_heap.insert(v)
min_heap.display()
print("Deleting root...")
min_heap.delete_root()
min_heap.display()

Output:
Enter at least 10 numbers separated by spaces: 5 2 79 34 27 4 6 9 1 7
----- Min Heap Demo -----
Min Heap: [1, 2, 4, 5, 7, 79, 6, 34, 9, 27]
Deleting root...
Min Heap: [2, 5, 4, 9, 7, 79, 6, 34, 27]
4. Construct Max Heap using arrays, delete any element and display the
content of the Heap.
Source code:

# Max Heap class using similar logic


class MaxHeap:
def __init__(self):
[Link] = []
def parent(self, i):
return (i - 1) // 2
def left(self, i):
return 2 * i + 1
def right(self, i):
return 2 * i + 2
def swap(self, i, j):
[Link][i], [Link][j] = [Link][j], [Link][i]
def insert(self, val):
[Link](val)
i = len([Link]) - 1
# Bubble-up to maintain max heap property
while i != 0 and [Link][[Link](i)] < [Link][i]:
[Link](i, [Link](i))
i = [Link](i)
def heapify(self, i):
n = len([Link])
largest = i
l = [Link](i)
r = [Link](i)
if l < n and [Link][l] > [Link][largest]:
largest = l
if r < n and [Link][r] > [Link][largest]:
largest = r
if largest != i:
[Link](i, largest)
[Link](largest)
def delete_root(self):
n = len([Link])
if n == 0:
print("Heap is empty")
return
[Link][0] = [Link][-1]
[Link]()
if len([Link]) > 0:
[Link](0)
def display(self):
print("Max Heap:", [Link])
# main
arr = [int(x) for x in input("Enter at least 10 numbers separated by spaces: ").split()]
print("\n----- Max Heap Demo -----")
max_heap = MaxHeap()
for v in arr:
max_heap.insert(v)
max_heap.display()
print("Deleting root...")
max_heap.delete_root()
max_heap.display()

Output:
Enter at least 10 numbers separated by spaces: 5 2 79 34 27 4 6 9 1 7

----- Max Heap Demo -----


Max Heap: [79, 34, 6, 9, 27, 4, 5, 2, 1, 7]
Deleting root...
Max Heap: [34, 27, 6, 9, 7, 4, 5, 2, 1]
5. Implement BFT and DFT for given graph, when graph is represented by
a) Adjacency Matrix

Source code:
from collections import deque
def bfs(adj_matrix, start_vertex):
visited = [False] * len(adj_matrix)
queue = deque()

print("BFT (BFS Traversal):", end=" ")


[Link](start_vertex)
visited[start_vertex] = True

while queue:
vertex = [Link]()
print(vertex, end=" ")

for i in range(len(adj_matrix)):
if adj_matrix[vertex][i] == 1 and not visited[i]:
[Link](i)
visited[i] = True
print()

# Function to perform Depth First Traversal


def dfs(adj_matrix, start_vertex, visited=None):
if visited is None:
visited = [False] * len(adj_matrix)
print("DFT (DFS Traversal):", end="--> ")

visited[start_vertex] = True
print(start_vertex, end=" ")

for i in range(len(adj_matrix)):
if adj_matrix[start_vertex][i] == 1 and not visited[i]:
dfs(adj_matrix, i, visited)
# Main function
def main():
# Input number of vertices
n = int(input("Enter number of vertices: "))

# Input adjacency matrix


print("Enter the adjacency matrix (row-wise):")
adj_matrix = []
for i in range(n):
row = list(map(int, input(f"Row {i}: ").split()))
adj_matrix.append(row)

# Input starting vertex


start_vertex = int(input("Enter the starting vertex (0 to {}): ".format(n - 1)))

# Perform BFS and DFS


bfs(adj_matrix, start_vertex)
dfs(adj_matrix, start_vertex)
print() # Newline for clean output

if __name__ == "__main__":
main()
Output:

Enter number of vertices: 7

Enter the adjacency matrix (row-wise):

Row 0: 0 1 0 1 1 0 0

Row 1: 1 0 1 0 1 0 0

Row 2: 0 1 0 0 1 1 1

Row 3: 1 0 0 0 1 0 0

Row 4: 1 1 1 1 0 1 0
Row 5: 0 0 1 0 1 0 1

Row 6: 0 0 1 0 0 1 0

Enter the starting vertex (0 to 6): 0

BFT (BFS Traversal): 0 1 3 4 2 5 6

DFT (DFS Traversal): 0 1 2 4 3 5 6

b) Adjacency Lists
Source code:
from collections import deque

# Function to perform Breadth-First Traversal


def bfs(adj_list, start_vertex):
visited = [False] * len(adj_list)
queue = deque()

print("BFT (BFS Traversal):", end=" ")


[Link](start_vertex)
visited[start_vertex] = True

while queue:
vertex = [Link]()
print(vertex, end=" ")

for neighbor in adj_list[vertex]:


if not visited[neighbor]:
[Link](neighbor)
visited[neighbor] = True
print()

# Function to perform Depth-First Traversal


def dfs(adj_list, start_vertex, visited=None):
if visited is None:
visited = [False] * len(adj_list)
print("DFT (DFS Traversal):", end=" ")

visited[start_vertex] = True
print(start_vertex, end=" ")

for neighbor in adj_list[start_vertex]:


if not visited[neighbor]:
dfs(adj_list, neighbor, visited)

# Main function
def main():
# Input number of vertices
n = int(input("Enter number of vertices: "))

# Create adjacency list


adj_list = [[] for _ in range(n)]

# Input edges
e = int(input("Enter number of edges: "))
print("Enter each edge as two vertices (u v):")
for _ in range(e):
u, v = map(int, input().split())
adj_list[u].append(v)
adj_list[v].append(u) # Remove this line for directed graph

# Input starting vertex


start_vertex = int(input(f"Enter the starting vertex (0 to {n - 1}): "))

# Perform BFS and DFS


bfs(adj_list, start_vertex)
dfs(adj_list, start_vertex)
print()
if __name__ == "__main__":
main()

Output:

Enter number of vertices: 5

Enter number of edges: 6

Enter each edge as two vertices (u v):

01

03

04

12

14

24

Enter the starting vertex (0 to 4): 0

BFT (BFS Traversal): 0 1 3 4 2

DFT (DFS Traversal): 0 1 2 4 3


6. Write a program for finding the bi-connected components in a given
graph.
Source code:
def is_biconnected(adj_matrix):
n = len(adj_matrix)
disc = [0] * n
low = [0] * n
parent = [-1] * n
time = [1] # start discovery time from 1
articulation_points = [False] * n

def dfs(u):
children = 0
disc[u] = low[u] = time[0]
time[0] += 1

for v in range(n):
if adj_matrix[u][v] == 1:
if disc[v] == 0: # unvisited
parent[v] = u
children += 1
dfs(v)
low[u] = min(low[u], low[v])

if parent[u] == -1 and children > 1:


articulation_points[u] = True
if parent[u] != -1 and low[v] >= disc[u]:
articulation_points[u] = True
elif v != parent[u]:
low[u] = min(low[u], disc[v])

dfs(0)

if any(d == 0 for d in disc):


return False, disc, low
if any(articulation_points):
return False, disc, low
return True, disc, low

# Given adjacency matrix from your example


adj_matrix = [
[0, 1, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 1],
[0, 1, 1, 0]
]

result, disc, low = is_biconnected(adj_matrix)


print("Is graph biconnected? ", result)
print("Discovery times:", disc)
print("Lowest reachable:", low)

Output:
Is graph biconnected? True
Discovery times: [1, 2, 3, 4]
Lowest reachable: [1, 1, 1, 2]
7. Implement Quick sort and observe the execution time for various input
sizes (Average, Worst and Best cases).
Source code:
def swap(a,b,arr):
if a!=b:
temp=arr[a]
arr[a]=arr[b]
arr[b]=temp
def partition(elements,start,end):
pivot_index=start
pivot=elements[pivot_index]
while start<end:
while start<len(elements) and elements[start]<=pivot:
start+=1
while elements[end]>pivot:
end-=1
if start<end:
swap(start,end,elements)
swap(pivot_index,end,elements)
return end

def quick_sort(elements,start,end):
if start<end:
pi=partition(elements,start,end)
quick_sort(elements,start,pi-1)#left partiton
quick_sort(elements,pi+1,end)#right partiton

if __name__=="__main__":
elements = []
num_elements = int(input("Enter the number of elements: "))
for i in range(num_elements):
element = int(input(f"Enter element {i+1}: "))
[Link](element)
quick_sort(elements, 0, len(elements) - 1)
print("Sorted array:", elements)
Output:
Enter the number of elements: 5
Enter element 1: 56
Enter element 2: 12
Enter element 3: 56
Enter element 4: 67
Enter element 5: 89
Sorted array: [12, 56, 56, 67, 89]
8. Implement Merge sort and observe the execution time for various input
sizes (Average, Worst and Best cases).
Source code:
def merge_sort(arr):
# Base case: A list of 0 or 1 elements is already sorted
if len(arr) <= 1:
return arr

# Split array into two halves


mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])

# Merge the sorted halves


return merge(left, right)

def merge(left, right):


merged = []
i=j=0

# Compare elements and merge


while i < len(left) and j < len(right):
if left[i] < right[j]:
[Link](left[i])
i += 1
else:
[Link](right[j])
j += 1

# Add remaining elements


[Link](left[i:])
[Link](right[j:])

return merged
# Example usage
arr = [38, 27, 43, 3, 9, 82, 10]
print("Original:", arr)
sorted_arr = merge_sort(arr)
print("Sorted:", sorted_arr)

Output:
Original: [38, 27, 43, 3, 9, 82, 10]
Sorted: [3, 9, 10, 27, 38, 43, 82]
9. Single Source Shortest Paths using Greedy method when the graph is
represented by adjacency matrix.
Source code:
import heapq, time
# -------------------------------
# Dijkstra using Adjacency Matrix
# -------------------------------
def dijkstra_matrix(graph, src):
n = len(graph)
dist = [float("inf")] * n
visited = [False] * n
dist[src] = 0

for _ in range(n):
# Greedy choice: pick nearest unvisited node
u = -1
min_dist = float("inf")
for i in range(n):
if not visited[i] and dist[i] < min_dist:
u=i
min_dist = dist[i]

visited[u] = True

# Relaxation step
for v in range(n):
if graph[u][v] != 0 and not visited[v]:
if dist[u] + graph[u][v] < dist[v]:
dist[v] = dist[u] + graph[u][v]
return dist

# -------------------------------
# Example Graph
# -------------------------------
matrix = [
[0, 10, 0, 5, 0],
[10, 0, 1, 2, 0],
[0, 1, 0, 9, 4],
[5, 2, 9, 0, 2],
[0, 0, 4, 2, 0]
]

src = 0

start = time.perf_counter()
dist_matrix = dijkstra_matrix(matrix, src)
end = time.perf_counter()
print("Matrix Distances:", dist_matrix)
print("Time with Adjacency Matrix: {:.6f} seconds".format(end - start))
Output:
List Distances : [0, 7, 8, 5, 7]
Time with Adjacency List : 0.000036 seconds
10. Single Source Shortest Paths using Greedy method when the graph is
represented by adjacency lists.
Source code:
import heapq, time
# -------------------------------
# Dijkstra using Adjacency List
# -------------------------------
def dijkstra_list(graph, src):
n = len(graph)
dist = [float("inf")] * n
dist[src] = 0
pq = [(0, src)] # (distance, vertex)

while pq:
d, u = [Link](pq)
if d > dist[u]:
continue
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
[Link](pq, (dist[v], v))
return dist

# -------------------------------
# Example Graph
# -------------------------------
adj_list = [
[(1, 10), (3, 5)],
[(0, 10), (2, 1), (3, 2)],
[(1, 1), (3, 9), (4, 4)],
[(0, 5), (1, 2), (2, 9), (4, 2)],
[(2, 4), (3, 2)]
]
src = 0
start = time.perf_counter()
dist_list = dijkstra_list(adj_list, src)
end = time.perf_counter()
print("List Distances :", dist_list)
print("Time with Adjacency List : {:.6f} seconds".format(end - start))

Output:
List Distances : [0, 7, 8, 5, 7]
Time with Adjacency List : 0.000018 seconds
11. Implement Job sequencing with deadlines using Greedy strategy.
Source code:
class Job:
def __init__(self, job_id, deadline, profit):
self.job_id = job_id
[Link] = deadline
[Link] = profit

def job_sequencing(jobs, n):


# Sort all jobs according to profit in descending order
[Link](key=lambda x: [Link], reverse=True)

# To keep track of free time slots


result = [False] * n

# To store result (sequence of jobs)


job_sequence = ['-1'] * n
# To store total profit
total_profit = 0

# Iterate through all jobs


for i in range(len(jobs)):
# Find a free slot for this job (from its deadline - 1 to 0)
for j in range(min(n, jobs[i].deadline) - 1, -1, -1):
if not result[j]:
result[j] = True
job_sequence[j] = jobs[i].job_id
total_profit+=jobs[i].profit
break

# Print the job sequence for maximum profit


print("Job Sequence:", job_sequence)
print("total profit:", total_profit)

if __name__ == "__main__": # Example usage


jobs = [
Job('J1', 2, 100),
Job('J2', 1, 19),
Job('J3', 2, 27),
Job('J4', 1, 25),
Job('J5', 3, 15)
]
n = 3 # Number of available slots (can also be max deadline)
job_sequencing(jobs, n)

Output:
Job Sequence: ['J3', 'J1', 'J5']
total profit: 142
12. Write a program to solve 0/1 Knapsack problem Using Dynamic
Programming.
Source code:
#0/1 Knapsack problem Using Dynamic Programming.

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


# Create DP table
K = [[0 for x in range(W + 1)] for x in range(n + 1)]

# Build table K[][] in bottom-up manner


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]

if __name__ == "__main__": # Example usage


profit = [60, 100, 120] # values
weight = [10, 20, 30] # weights
W = 50 # capacity of knapsack
n = len(profit)

max_profit = knapsack(W, weight, profit, n)


print("Maximum profit in Knapsack =", max_profit)

Output:
Maximum profit in Knapsack = 220
13. Implement N-Queens Problem Using Backtracking.
Source code:
def solve_nqueens(N):
board = [[0] * N for _ in range(N)]

def is_safe(row, col):


# Check column
for i in range(row):
if board[i][col] == 1:
return False
# Check upper-left diagonal
i, j = row - 1, col - 1
while i >= 0 and j >= 0:
if board[i][j] == 1:
return False
i -= 1
j -= 1
# Check upper-right diagonal
i, j = row - 1, col + 1
while i >= 0 and j < N:
if board[i][j] == 1:
return False
i -= 1
j += 1
return True

def place_queen(row):
if row == N:
for r in range(N):
for c in range(N):
print("Q" if board[r][c] else ".", end=" ")
print()
print()
return True
found = False
for col in range(N):
if is_safe(row, col):
board[row][col] = 1
found = place_queen(row + 1) or found
board[row][col] = 0 # backtrack
return found

if not place_queen(0):
print("No solution exists for N =", N)

# Example usage
if __name__ == "__main__":
N=4
solve_nqueens(N)

Output:
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..
14. Use Backtracking strategy to solve 0/1 Knapsack problem.
Source code:
def knapsack_backtrack(wt, val, W, n, idx=0, curr_profit=0, curr_weight=0,
best=[0]):
# Base case: all items considered
if idx == n:
best[0] = max(best[0], curr_profit)
return

# Choice 1: Exclude the current item


knapsack_backtrack(wt, val, W, n, idx + 1, curr_profit, curr_weight, best)

# Choice 2: Include the current item (if it fits)


if curr_weight + wt[idx] <= W:
knapsack_backtrack(
wt, val, W, n,
idx + 1,
curr_profit + val[idx],
curr_weight + wt[idx],
best )

def solve_knapsack(wt, val, W):


n = len(wt)
best = [0] # store maximum profit found
knapsack_backtrack(wt, val, W, n, 0, 0, 0, best)
return best[0]

if __name__ == "__main__": # Example usage


profit = [60, 100, 120] # values
weight = [10, 20, 30] # weights
W = 50 # capacity of knapsack
max_profit = solve_knapsack(weight, profit, W)
print("Maximum profit using Backtracking =", max_profit)

Output:
Maximum profit using Backtracking = 220
15. Implement Travelling Sales Person problem using Branch and Bound
approach.
Source code:
import math
class TSPSolver:
def __init__(self, graph):
[Link] = graph
self.n = len(graph)
self.final_res = float("inf")
self.final_path = []

# Copy temporary solution to final path


def copy_to_final(self, curr_path):
self.final_path = curr_path[:] + [curr_path[0]]

# Find minimum edge cost having an end at vertex i


def first_min(self, i):
min_val = float("inf")
for k in range(self.n):
if [Link][i][k] != 0 and [Link][i][k] < min_val:
min_val = [Link][i][k]
return min_val

# Find second minimum edge cost having an end at vertex i


def second_min(self, i):
first, second = float("inf"), float("inf")
for j in range(self.n):
if i == j:
continue
if [Link][i][j] <= first:
second = first
first = [Link][i][j]
elif [Link][i][j] <= second and [Link][i][j] != first:
second = [Link][i][j]
return second

# Recursive function for Branch and Bound


def tsp_rec(self, curr_bound, curr_weight, level, curr_path, visited):
# Base case: all vertices visited
if level == self.n:
if [Link][curr_path[level - 1]][curr_path[0]] != 0:
curr_res = curr_weight + [Link][curr_path[level -1]][curr_path[0]]
if curr_res < self.final_res:
self.copy_to_final(curr_path)
self.final_res = curr_res
return

# For current vertex, explore all neighbors


for i in range(self.n):
if ([Link][curr_path[level - 1]][i] != 0 and not visited[i]):
temp = curr_bound
curr_weight += [Link][curr_path[level - 1]][i]

# Compute new lower bound


if level == 1:
curr_bound -= ((self.first_min(curr_path[level - 1]) + self.first_min(i)) / 2)
else:
curr_bound -= ((self.second_min(curr_path[level - 1]) + self.first_min(i)) /
2)

# If promising, recurse
if curr_bound + curr_weight < self.final_res:
curr_path[level] = i
visited[i] = True
self.tsp_rec(curr_bound, curr_weight, level + 1, curr_path, visited)

# Backtrack
curr_weight -= [Link][curr_path[level - 1]][i]
curr_bound = temp
visited[i] = False

# Solve TSP
def solve(self):
curr_bound = 0
curr_path = [-1] * (self.n + 1)
visited = [False] * self.n

# Compute initial bound (sum of first and second min for each vertex)
for i in range(self.n):
curr_bound += (self.first_min(i) + self.second_min(i))

curr_bound = [Link](curr_bound / 2)

visited[0] = True
curr_path[0] = 0

self.tsp_rec(curr_bound, 0, 1, curr_path, visited)

print("Minimum cost:", self.final_res)


print("Path taken:", " -> ".join(map(str, self.final_path)))

if __name__ == "__main__": # Example usage


# Graph represented as adjacency matrix
graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]

solver = TSPSolver(graph)
[Link]()
Output:
Minimum cost: 80
Path taken: 0 -> 1 -> 3 -> 2 -> -1 -> 0

You might also like