0% found this document useful (0 votes)
3 views59 pages

Adsa Manual

The document outlines various implementations of data structures and algorithms in Python, including Fibonacci numbers using recursion and iteration, merge sort, quick sort, binary search trees, red-black trees, heaps, and Fibonacci heaps. Each section contains an aim, algorithm, program code, example usage, and results confirming successful execution. The document serves as a comprehensive guide for implementing these algorithms and data structures.

Uploaded by

Hari Priyan
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)
3 views59 pages

Adsa Manual

The document outlines various implementations of data structures and algorithms in Python, including Fibonacci numbers using recursion and iteration, merge sort, quick sort, binary search trees, red-black trees, heaps, and Fibonacci heaps. Each section contains an aim, algorithm, program code, example usage, and results confirming successful execution. The document serves as a comprehensive guide for implementing these algorithms and data structures.

Uploaded by

Hari Priyan
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
You are on page 1/ 59

Ex.

No: 1 IMPLEMENTATION OF FIBONACCI NUMBER


USING RECURSION
Date:

AIM:

To write a python program to implement Fibonacci number using Recursion.

ALGORITHM:

● The Fibonacci function takes an integer n as input and returns the nth
Fibonacci number.
● It uses recursion to calculate the Fibonacci number based on the algorithm
you provided.

1
PROGRAM:

def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)

# Example usage
n = int(input("Enter the value of n for Fibonacci series: "))
result = fibonacci(n)
print(f"The {n}th Fibonacci number is: {result}")

2
OUTPUT

Enter the value of n for Fibonacci series: 8


The 8th Fibonacci number is: 21

RESULT:

Thus the python program to implement Fibonacci number using Recursion was written,
executed and Verified successfully.

3
Ex.No: 2 IMPLEMENTATION OF FIBONACCI NUMBER
USING ITERATION
Date:

AIM:

To write a python program to implement Fibonacci number using Iteration.

ALGORITHM:

● The Node class represents a node in the binary tree.


● The preorder function performs a preorder traversal of the binary tree and
prints the data of each node.

4
PROGRAM:

class Node:
def __init__(self, key):
self.data = key
self.left = self.right = None

def preorder(root):
if root is not None:
print(root.data, end=" ")
preorder(root.left)
preorder(root.right)

# Example usage
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.right.left = Node(5)
root.right.right = Node(6)
root.right.left.left = Node(7)
root.right.left.right = Node(8)

print("Preorder Traversal:")
preorder(root)

5
OUTPUT

Preorder Traversal:
12435786

RESULT:

Thus the python program to implement Fibonacci number using Recursion was written,
executed and Verified successfully.

6
Ex.No: 3a IMPLEMENTATION OF MERGE SORT
ANALYSIS
Date:

AIM:

To write a python program to implement merge sort analysis.

ALGORITHM:

● The merge_sort function recursively divides the array into halves until it
contains only one element.
● The merge function is responsible for merging two sorted halves into a single
sorted array.

7
PROGRAM:

def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2 # Calculate the middle index

# Divide the array into two halves


left_half = arr[:mid]
right_half = arr[mid:]

# Recursive calls to sort each half


merge_sort(left_half)
merge_sort(right_half)

# Merge the sorted halves


merge(arr, left_half, right_half)

def merge(arr, left, right):


i=j=k=0

# Compare elements from left and right halves and merge in sorted order
while i < len(left) and j < len(right):
if left[i] < right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
k += 1

# Copy the remaining elements from left and right halves, if any
while i < len(left):
arr[k] = left[i]
i += 1
k += 1

while j < len(right):


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

# Example usage and analysis


arr = [38, 27, 43, 3, 9, 82, 10]
print("Original array:", arr)

merge_sort(arr)

print("Sorted array:", arr)

8
OUTPUT

Original array: [38, 27, 43, 3, 9, 82, 10]


Sorted array: [3, 9, 10, 27, 38, 43, 82]

RESULT:

Thus the python program to implement Fibonacci number using Recursion was written,
executed and Verified successfully.

9
Ex.No: 3b IMPLEMENTATION OF QUICK SORT
ANALYSIS
Date:

AIM:

To write a python program to implement merge sort analysis.

ALGORITHM:

● The quick_sort function recursively applies the quicksort algorithm.


● It chooses a pivot element from the array, partitions the array into elements
smaller than the pivot, equal to the pivot, and greater than the pivot, and then
recursively sorts the subarrays.

10
PROGRAM:

def quick_sort(arr):
if len(arr) <= 1:
return arr # Base case: already sorted if the array has 0 or 1 element
else:
pivot = arr[len(arr) // 2] # Choose the middle element as the pivot
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)

# Example usage and analysis


arr = [38, 27, 43, 3, 9, 82, 10]
print("Original array:", arr)

sorted_array = quick_sort(arr)

print("Sorted array:", sorted_array)

11
OUTPUT

Original array: [38, 27, 43, 3, 9, 82, 10]


Sorted array: [3, 9, 10, 27, 38, 43, 82]

RESULT:

Thus the python program to implement Fibonacci number using Recursion was written,
executed and Verified successfully.

12
Ex.No: 4 IMPLEMENTATION OF BINARY SEARCH TREE

Date:

AIM:

To write a python program for implementing Binary Search Tree.

ALGORITHM:

● The Node class represents a node in the binary search tree.


● The insert function inserts a new node into the BST based on the given key.
● The search function searches for a key in the BST and returns the node if
found.

13
PROGRAM:

class Node:
def __init__(self, key):
self.key = key
self.left = self.right = None

def insert(root, key):


if root is None:
return Node(key)
else:
if key < root.key:
root.left = insert(root.left, key)
elif key > root.key:
root.right = insert(root.right, key)
return root

def search(root, key):


if root is None or root.key == key:
return root
if key < root.key:
return search(root.left, key)
return search(root.right, key)

# Example usage and testing


root = None
keys = [50, 30, 20, 40, 70, 60, 80]

for key in keys:


root = insert(root, key)

search_key = int(input("Enter the search element: "))


result = search(root, search_key)

if result:
print(f"Element {search_key} found in the BST.")
else:
print(f"Element {search_key} not found in the BST.")

14
OUTPUT

Enter the search element: 60


Element 60 found in the BST.

.
RESULT:

Thus the implementation of Binary Search tree was written, executed and verified
successfully.

15
Ex.No: 5 RED BLACK TREE IMPLEMENTATION

Date:

AIM:

To write a python program to implement Red-Black Tree.

ALGORITHM:

● The insert method is used to insert elements into the Red-Black Tree.
● The inorder_traversal method is used to visualize the Red-Black Tree using
inorder traversal.

16
PROGRAM:

class Node:
def __init__(self, key, color='R'):
self.key = key
self.left = self.right = self.parent = None
self.color = color

class RedBlackTree:
def __init__(self):
self.NIL = Node(None, 'B') # NIL represents a null node
self.root = self.NIL

def insert(self, key):


# Step 1: Check whether the tree is empty
if self.root == self.NIL:
self.root = Node(key, 'B') # Insert the newNode as the root with color Black
else:
# Step 2: Insert the newNode as a leaf node with Red color
new_node = Node(key, 'R')
self._insert(new_node)

def _insert(self, node):


y = None
x = self.root

# Perform a regular binary search tree insert


while x != self.NIL:
y=x
if node.key < x.key:
x = x.left
else:
x = x.right

node.parent = y
if y == None:
self.root = node
elif node.key < y.key:
y.left = node
else:
y.right = node

# Insert may violate the Red-Black Tree properties, fix it


self._insert_fixup(node)

def _insert_fixup(self, node):

17
while node.parent and node.parent.color == 'R':
if node.parent == node.parent.parent.left:
y = node.parent.parent.right
if y and y.color == 'R':
node.parent.color = 'B'
y.color = 'B'
node.parent.parent.color = 'R'
node = node.parent.parent
else:
if node == node.parent.right:
node = node.parent
self.left_rotate(node)

node.parent.color = 'B'
node.parent.parent.color = 'R'
self.right_rotate(node.parent.parent)
else:
y = node.parent.parent.left
if y and y.color == 'R':
node.parent.color = 'B'
y.color = 'B'
node.parent.parent.color = 'R'
node = node.parent.parent
else:
if node == node.parent.left:
node = node.parent
self.right_rotate(node)

node.parent.color = 'B'
node.parent.parent.color = 'R'
self.left_rotate(node.parent.parent)

self.root.color = 'B' # Ensure the root is black

def left_rotate(self, x):


y = x.right
x.right = y.left

if y.left != self.NIL:
y.left.parent = x

y.parent = x.parent

if x.parent == None:
self.root = y
elif x == x.parent.left:
x.parent.left = y
else:

18
x.parent.right = y

y.left = x
x.parent = y

def right_rotate(self, x):


y = x.left
x.left = y.right

if y.right != self.NIL:
y.right.parent = x

y.parent = x.parent

if x.parent == None:
self.root = y
elif x == x.parent.right:
x.parent.right = y
else:
x.parent.left = y

y.right = x
x.parent = y

def inorder_traversal(self, node):


if node != self.NIL:
self.inorder_traversal(node.left)
print(f'{node.key} ({node.color}) ', end='')
self.inorder_traversal(node.right)

# Example usage
rb_tree = RedBlackTree()
keys = [50, 30, 20, 40, 70, 60, 80]

for key in keys:


rb_tree.insert(key)

print("Inorder Traversal of Red-Black Tree:")


rb_tree.inorder_traversal(rb_tree.root)

19
OUTPUT

Inorder Traversal of Red-Black Tree:


20 (B) 30 (B) 40 (R) 50 (B) 60 (R) 70 (B) 80 (R)

RESULT:

Thus the python program to implement Red Black Tree was executed and verified
successfully.

20
Ex.No: 6 HEAP IMPLEMENTATION

Date:

AIM:

To write a python program for heap implementation.

ALGORITHM:

● buildMaxHeap() is used to convert the array into a max heap.


● heapify() is a helper function to maintain the heap property during the build
process.
● heapSort() is the main sorting function that uses the max heap structure.

21
PROGRAM:

def buildMaxHeap(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)

def heapify(arr, n, i):


largest = i
left = 2 * i + 1
right = 2 * i + 2

if left < n and arr[left] > arr[largest]:


largest = left

if right < n and arr[right] > arr[largest]:


largest = right

if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)

def heapSort(arr):
n = len(arr)

# Build max heap


buildMaxHeap(arr)

# Extract elements one by one


for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # Swap
heapify(arr, i, 0)

# Example usage and result


arr = [12, 11, 13, 5, 6, 7]
print("Original array:", arr)

heapSort(arr)

print("Sorted array:", arr)

22
OUTPUT

Original array: [12, 11, 13, 5, 6, 7]


Sorted array: [5, 6, 7, 11, 12, 13]

RESULT:

Thus the heap implementation was written, executed and verified successfully.

23
Ex.No: 7 FIBONACCI HEAP IMPLEMENTATION

Date:

AIM:

To write a python program for Fibonacci heap implement.

ALGORITHM:

Initialization:
● FibonacciNode class represents a node in the Fibonacci Heap.
● FibonacciHeap class represents the Fibonacci Heap itself.
● Each node has a key, degree, marked status, references to child, parent,
next, and prev nodes.
● The heap has a min_node representing the minimum node and keeps
track of the number of nodes.
Insertion (insert method):
● Inserts a new node with the given key into the heap.
● If the heap is empty, the new node becomes the min_node.
● Otherwise, the new node is linked into the circular doubly linked list
using the _link method, and if its key is smaller than the current
minimum, it becomes the new minimum.
Finding Minimum (minimum method):
● Returns the key of the minimum node in the heap.
Extracting Minimum (extractMin method):
● Removes and returns the node with the minimum key from the heap.
● If the minimum node has children, they are added to the root list.
● The minimum node is removed from the root list, and consolidation is
performed using the _consolidate method to ensure that there is at
most one tree of each degree.
Decreasing Key (decreaseKey method):
● Decreases the key of a node to a new value.
● If the new key is smaller than the current key, the node is cut from its
parent and possibly marked. Cascading cuts are performed using the
_cut and _cascading_cut methods.
Deletion (delete method):
● Deletes a node from the heap by decreasing its key to negative infinity
and then extracting the minimum.
Union (union method):
● Merges another Fibonacci Heap with the current heap by linking their
root lists.

24
Linking (_link method):
● Links two trees of the same degree together.
Cutting (_cut method):
● Cuts the link between a child and its parent, making the child a new
root.
Cascading Cut (_cascading_cut method):
● Ensures that no tree in the heap has lost two children by performing
cascading cuts.
Consolidation (_consolidate method):
● Consolidates trees of the same degree to ensure there is at most one
tree of each degree.
Example Usage and Output:
● The code includes an example where keys are inserted into the
Fibonacci Heap, the minimum key is printed, and then keys are
extracted in increasing order.

Feel free to use this explanation as a guide for your lab report or to ask any specific
questions you might have!

25
PROGRAM:

class FibonacciNode:
def __init__(self, key):
self.key = key
self.degree = 0
self.marked = False
self.child = None
self.parent = None
self.next = self
self.prev = self

class FibonacciHeap:
def __init__(self):
self.min_node = None
self.num_nodes = 0

def insert(self, key):


new_node = FibonacciNode(key)
if self.min_node is None:
self.min_node = new_node
else:
self._link(self.min_node, new_node)
if key < self.min_node.key:
self.min_node = new_node
self.num_nodes += 1

def minimum(self):
return self.min_node.key if self.min_node else None

def extractMin(self):
min_node = self.min_node
if min_node:
if min_node.child:
child = min_node.child
while child.parent:
next_child = child.next
min_node.child = next_child
self._add_node(child)
child.parent = None
child = next_child

prev_node = min_node.prev
next_node = min_node.next

26
prev_node.next = next_node
next_node.prev = prev_node

if min_node == min_node.next:
self.min_node = None
else:
self.min_node = next_node
self._consolidate()

self.num_nodes -= 1

return min_node.key if min_node else None

def decreaseKey(self, node, new_key):


if new_key > node.key:
raise ValueError("New key is greater than current key")

node.key = new_key
parent = node.parent

if parent and node.key < parent.key:


self._cut(node, parent)
self._cascading_cut(parent)

if node.key < self.min_node.key:


self.min_node = node

def delete(self, node):


self.decreaseKey(node, float('-inf'))
self.extractMin()

def union(self, other_heap):


if other_heap.min_node:
if self.min_node:
self._link(self.min_node, other_heap.min_node)
if other_heap.min_node.key < self.min_node.key:
self.min_node = other_heap.min_node
else:
self.min_node = other_heap.min_node
self.num_nodes += other_heap.num_nodes

def _link(self, root1, root2):


root2.prev.next = root1.next
root1.next.prev = root2.prev
root1.next = root2
root2.prev = root1

def _add_node(self, node):

27
node.next = node
node.prev = node

def _cut(self, child, parent):


if child.next == child:
parent.child = None
else:
next_child = child.next
prev_child = child.prev
next_child.prev = prev_child
prev_child.next = next_child

if parent.child == child:
parent.child = next_child

parent.degree -= 1
self._add_node(child)
child.parent = None
child.marked = False

def _cascading_cut(self, node):


parent = node.parent
if parent:
if not node.marked:
node.marked = True
else:
self._cut(node, parent)
self._cascading_cut(parent)

def _consolidate(self):
max_degree = int(self.num_nodes**0.5) + 1
degree_array = [None] * max_degree

current = self.min_node
nodes = []

while nodes.append(current) and current != self.min_node:


current = current.next

for node in nodes:


degree = node.degree
while degree_array[degree]:
other = degree_array[degree]
if node.key > other.key:
node, other = other, node
self._link(other, node)
degree_array[degree] = None
degree += 1

28
degree_array[degree] = node

self.min_node = None
for degree_node in degree_array:
if degree_node:
if self.min_node:
self._link(self.min_node, degree_node)
if degree_node.key < self.min_node.key:
self.min_node = degree_node
else:
self.min_node = degree_node

# Example usage and output for lab practice


fib_heap = FibonacciHeap()
keys = [4, 3, 7, 2, 8, 1, 6, 5]

print("Inserting keys into Fibonacci Heap:", keys)


for key in keys:
fib_heap.insert(key)

print("Minimum key in the Fibonacci Heap:", fib_heap.minimum())

print("Extracting Min from the Fibonacci Heap:")


while fib_heap.minimum() is not None:
print(fib_heap.extractMin())

29
OUTPUT

Inserting keys into Fibonacci Heap: [4, 3, 7, 2, 8, 1, 6, 5]


Minimum key in the Fibonacci Heap: 1
Extracting Min from the Fibonacci Heap:
1
2
3
4
5
6
7
8

RESULT:

Thus the Fibonacci heap implementation was written, executed and verified successfully.

30
Ex.No: 8a GRAPH TRAVERSALS (DEPTH FIRST SEARCH)

Date:

AIM:

To write a python program to implement graph traversal by using depth first search.

ALGORITHM:

• The Graph class uses an adjacency list to represent the graph.


• The add_edge method adds edges to the graph.
• The dfs method performs Depth First Search starting from a given node.
• A stack is used to keep track of nodes to be visited.
• The algorithm explores as far as possible along each branch before from
collections.

31
PROGRAM:

class Graph:
def __init__(self):
self.graph = defaultdict(list)

def add_edge(self, u, v):


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

def dfs(self, start):


visited = set() # To keep track of visited nodes
stack = [start] # Stack for DFS traversal

while stack:
current_node = stack.pop()
if current_node not in visited:
print(current_node, end=" ")
visited.add(current_node)

# Add adjacent nodes to the stack


stack.extend(neighbor for neighbor in self.graph[current_node] if neighbor not in
visited)

# Example Usage and Output for Lab Practice


graph = Graph()
graph.add_edge(1, 2)
graph.add_edge(1, 3)
graph.add_edge(2, 4)
graph.add_edge(2, 5)
graph.add_edge(3, 6)
graph.add_edge(3, 7)

print("Depth First Search (DFS) starting from node 1:")


graph.dfs(1)

32
OUTPUT:

Depth First Search (DFS) starting from node 1:


1376254

RESULT:

Thus the python program to implement graph traversal by using depth first search was
written and verified successfully.

33
Ex.No: 8b GRAPH TRAVERSALS (BREADTH FIRST SEARCH)

Date:

AIM:

To write a program to implement graph traversals by using breadth first search.

ALGORITHM:

• The Graph class uses an adjacency list to represent the graph.


• The add_edge method adds edges to the graph.
• The bfs method performs Breadth First Search starting from a given node.
• A queue is used to keep track of nodes to be visited.
• The algorithm visits all neighbors of the current node before moving on to the
next level.

34
PROGRAM :

from collections import defaultdict, deque

class Graph:
def __init__(self):
self.graph = defaultdict(list)

def add_edge(self, u, v):


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

def bfs(self, start):


visited = set() # To keep track of visited nodes
queue = deque([start]) # Queue for BFS traversal

while queue:
current_node = queue.popleft()
if current_node not in visited:
print(current_node, end=" ")
visited.add(current_node)

# Add adjacent nodes to the queue


queue.extend(neighbor for neighbor in self.graph[current_node] if neighbor not in
visited)

# Example Usage and Output for Lab Practice


graph = Graph()
graph.add_edge(1, 2)
graph.add_edge(1, 3)
graph.add_edge(2, 4)
graph.add_edge(2, 5)
graph.add_edge(3, 6)
graph.add_edge(3, 7)

print("Breadth First Search (BFS) starting from node 1:")


graph.bfs(1)

35
OUTPUT

Breadth First Search (BFS) starting from node 1:


1234567

RESULT:

Thus the python program to implement graph traversal by using breadth first search was
written, executed and verified successfully.

36
Ex.No: 9a
SPANNING TREE IMPLEMENTATION (KRUSKAL’S ALGORITHM)
Date:

AIM:

To write a python program to implement spanning tree implementation using kruskal‟s


algorithm.

ALGORITHM:

• The Graph class initializes the graph with a given number of vertices.
• The add_edge method adds edges to the graph.
• The find_parent and union methods are used for finding the parent and
union operations in the disjoint-set data structure.
• The kruskal method implements Kruskal's algorithm to find the minimum
spanning tree.
• The example usage creates a graph and finds the minimum spanning tree
using Kruskal's algorithm.

37
PROGRAM:

from collections import defaultdict, deque

class Graph:
def __init__(self):
self.graph = defaultdict(list)

def add_edge(self, u, v):


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

def bfs(self, start):


visited = set() # To keep track of visited nodes
queue = deque([start]) # Queue for BFS traversal

while queue:
current_node = queue.popleft()
if current_node not in visited:
print(current_node, end=" ")
visited.add(current_node)

# Add adjacent nodes to the queue


queue.extend(neighbor for neighbor in self.graph[current_node] if neighbor not in
visited)

# Example Usage and Output for Lab Practice


graph = Graph()
graph.add_edge(1, 2)
graph.add_edge(1, 3)
graph.add_edge(2, 4)
graph.add_edge(2, 5)
graph.add_edge(3, 6)
graph.add_edge(3, 7)

print("Breadth First Search (BFS) starting from node 1:")


graph.bfs(1)

38
OUTPUT

Minimum Spanning Tree (Kruskal's Algorithm):


Edge: 2 - 3, Weight: 4
Edge: 0 - 3, Weight: 5
Edge: 0 - 1, Weight: 10

RESULT:

Thus the Spanning tree implementation using kruskal‟s algorithm was written,
executed and verified successfully.

39
Ex.No: 9b
SPANNING TREE IMPLEMENTATION (PRIM’S ALGORITHM)
Date:

AIM:
To write a python program to implement spanning tree implementation using Prim‟s
algorithm.

ALGORITHM:

• The Graph class initializes the graph with a given number of vertices.
• The add_edge method adds edges to the graph.
• The prim method implements Prim's algorithm to find the minimum spanning
tree.
• The algorithm maintains a set of vertices (mst_set) included in the MST and
uses a min-heap to select the next vertex to be included.
• The example usage creates a graph and finds the weight of the minimum
spanning tree using Prim's algorithm.

40
PROGRAM:

import heapq

class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [[] for _ in range(vertices)]

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


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

def prim(self):
mst_set = set()
min_heap = [(0, 0)] # (key, vertex) - heap to store vertices with their key values
total_weight = 0
key_values = [float('inf')] * self.V
key_values[0] = 0

while len(mst_set) < self.V:


key, u = heapq.heappop(min_heap)
if u not in mst_set:
mst_set.add(u)
total_weight += key

for v, weight in self.graph[u]:


if v not in mst_set and weight < key_values[v]:
key_values[v] = weight
heapq.heappush(min_heap, (weight, v))

return total_weight

# Example Usage and Output for Lab Practice


g = Graph(5)
g.add_edge(0, 1, 2)
g.add_edge(0, 3, 6)
g.add_edge(1, 2, 3)
g.add_edge(1, 3, 8)
g.add_edge(1, 4, 5)
g.add_edge(2, 4, 7)
g.add_edge(3, 4, 9)

print("Minimum Spanning Tree (Prim's Algorithm) Weight:", g.prim())

41
OUTPUT:

Minimum Spanning Tree (Prim's Algorithm) Weight: 16

RESULT:

Thus the Spanning tree implementation using prim’s algorithm was written,
executed and verified successfully.

42
Ex.No: 10a
SHORTEST PATH ALGORITHM (DIJKSTRA’S ALGORITHM)
Date:

AIM:

To write a pythan program to implement Dijkstra‟s Algorithm.

ALGORITHM:

• The Graph class initializes the graph with a given number of vertices.
• The add_edge method adds edges to the graph.
• The dijkstra method implements Dijkstra's algorithm to find the shortest
distances from a given source vertex.
• The algorithm uses a min-heap to efficiently select the next vertex with the
smallest temporary distance.
• The example usage creates a graph and finds the shortest distances from a
specified starting vertex.

43
PROGRAM:

import heapq

class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [[] for _ in range(vertices)]

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


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

def dijkstra(self, start):


min_heap = [(0, start)] # (distance, vertex) - heap to store vertices with their distances
distances = [float('inf')] * self.V
distances[start] = 0

while min_heap:
dist_u, u = heapq.heappop(min_heap)

for v, weight in self.graph[u]:


new_dist = dist_u + weight
if new_dist < distances[v]:
distances[v] = new_dist
heapq.heappush(min_heap, (new_dist, v))

return distances

# Example Usage and Output for Lab Practice


g = Graph(6)
g.add_edge(0, 1, 4)
g.add_edge(0, 2, 2)
g.add_edge(1, 2, 5)
g.add_edge(1, 3, 10)
g.add_edge(2, 4, 3)
g.add_edge(3, 5, 7)
g.add_edge(4, 3, 1)
g.add_edge(4, 5, 8)

start_vertex = 0
shortest_distances = g.dijkstra(start_vertex)

print(f"Shortest Distances from Vertex {start_vertex}:")


for v, dist in enumerate(shortest_distances):
print(f"To Vertex {v}: {dist}")

44
OUTPUT

Shortest Distances from Vertex 0:


To Vertex 0: 0
To Vertex 1: 4
To Vertex 2: 2
To Vertex 3: 9
To Vertex 4: 5
To Vertex 5: 16

RESULT:

Thus the python program to implement Dijkstra’s Algorithm was executed and
verified successfully.

45
Ex.No: 10b
SHORTEST PATH ALGARITHMS (BELLMANN FORD ALGORITHM)
Date:

AIM:

To write a python program to implement Bellmann Ford algorithm.

ALGORITHM:

• The Graph class initializes the graph with a given number of vertices.
• The add_edge method adds edges to the graph.
• The bellman_ford method implements the Bellman-Ford algorithm to find the
shortest distances from a given source vertex.
• The algorithm iterates through all edges multiple times to relax the path
distances.
• It also checks for negative cycles by iterating through all edges one more
time.

46
PROGRAM:

class Graph:
def __init__(self, vertices):
self.V = vertices
self.edges = []

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


self.edges.append((u, v, w))

def bellman_ford(self, start):


distances = [float('inf')] * self.V
distances[start] = 0

for _ in range(self.V - 1):


for u, v, weight in self.edges:
if distances[u] != float('inf') and distances[u] + weight < distances[v]:
distances[v] = distances[u] + weight

# Check for negative cycles


for u, v, weight in self.edges:
if distances[u] != float('inf') and distances[u] + weight < distances[v]:
raise ValueError("Graph contains a negative cycle")

return distances

# Example Usage and Output for Lab Practice


g = Graph(5)
g.add_edge(0, 1, 4)
g.add_edge(0, 2, 2)
g.add_edge(1, 2, 5)
g.add_edge(1, 3, 10)
g.add_edge(2, 4, 3)
g.add_edge(3, 5, 7)
g.add_edge(4, 3, 1)
g.add_edge(4, 5, 8)

start_vertex = 0
shortest_distances = g.bellman_ford(start_vertex)

print(f"Shortest Distances from Vertex {start_vertex}:")


for v, dist in enumerate(shortest_distances):
print(f"To Vertex {v}: {dist}")

47
OUTPUT

Shortest Distances from Vertex 0:


To Vertex 0: 0
To Vertex 1: 4
To Vertex 2: 2
To Vertex 3: 9
To Vertex 4: 5
To Vertex 5: 16

RESULT:

Thus the python program to implement Bellmann Ford Algorithm was written,
executed and verified successfully

48
Ex.No: 11
MATRIX CHAIN MULTIPLICATION
Date:

AIM:

To write a python program to implement Matrix Chain Multiplication.

ALGORITHM:
• A chain of matrices to be multiplied is given as input.
• For a sequence A1,A2,A3,A4 of 4 matrices, there are 5 different orderings=5
different parenthesization.
o (A1,(A2(A3 A4)))
o (A1((A2 A3)A4))
o ((A1 A2)(A3 A4))
o ((A1(A2 A3))A4)
o (((A1 A2)A3)A4)
• Matrix_Multiply(A,B)
• If coloumns[A]!=rows[B]
• Then error “incomplete dimensions”
• Else for i <- 1 to rows[A]
• Do for j <- 1 to columns[B]
• Do c[I,j] <- 0
• For k<- 1 to columns[A]
• Do c[i,j]=C[i,j]+A[i,k]+B[i,j]
• Return c
• A parenthesizing of the chain of the matrices is obtained as output.

49
PROGRAM:

def matrix_chain_multiplication(p):
n = len(p) - 1 # Number of matrices
m = [[0] * (n + 1) for _ in range(n + 1)] # Table to store minimum multiplication cost
s = [[0] * (n + 1) for _ in range(n + 1)] # Table to store split points

for length in range(2, n + 1):


for i in range(1, n - length + 2):
j = i + length - 1
m[i][j] = float('inf')
for k in range(i, j):
cost = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]
if cost < m[i][j]:
m[i][j] = cost
s[i][j] = k

return m, s

def print_optimal_parenthesization(s, i, j):


if i == j:
print(f"A{i}", end="")
else:
print("(", end="")
print_optimal_parenthesization(s, i, s[i][j])
print_optimal_parenthesization(s, s[i][j] + 1, j)
print(")", end="")

# Example Usage and Output for Lab Practice


matrix_dimensions = [30, 35, 15, 5, 10, 20, 25]
m_table, s_table = matrix_chain_multiplication(matrix_dimensions)

print("Minimum Multiplication Cost Table:")


for row in m_table:
print(row)

print("\nOptimal Parenthesization:")
print_optimal_parenthesization(s_table, 1, len(matrix_dimensions) - 1)

50
OUTPUT:

Minimum Multiplication Cost Table:


[0, 15750, 7875, 9375, 11875, 15125]
[0, 0, 2625, 4375, 7125, 10500]
[0, 0, 0, 750, 2500, 5375]
[0, 0, 0, 0, 1000, 3500]
[0, 0, 0, 0, 0, 5000]
[0, 0, 0, 0, 0, 0]

Optimal Parenthesization:
((A1(A2A3))((A4A5)A6))

RESULT:

Thus the python program to implement Matrix Chain Multiplication was written, executed and
verified successfully.

51
Ex.No: 12a
ACTIVITY SELECTION
Date:

AIM:

To write a python program to implement Activity Selection.

ALGORITHM:

• Sort the activities as per finishing time in ascending order


• Select the first activity
• Select the new activity if its starting time is greater than or equal to the previously
selected
• activity
• REPEAT step 3 till all activities are checked.

52
PROGRAM:

def activity_selection(start, finish):


n = len(start)
activities = []

# Create a list of tuples (start_time, finish_time, activity_number)


for i in range(n):
activities.append((start[i], finish[i], i + 1))

# Sort activities based on finish times


activities.sort(key=lambda x: x[1])

# Select the first activity


selected_activities = [activities[0]]

# Select new activity if its start time is greater than or equal to the finish time of the last
selected activity
for i in range(1, n):
if activities[i][0] >= selected_activities[-1][1]:
selected_activities.append(activities[i])

return selected_activities

# Example Usage and Output for Lab Practice


start_times = [1, 3, 0, 5, 8, 5]
finish_times = [2, 4, 6, 7, 9, 9]

selected_activities = activity_selection(start_times, finish_times)

print("Selected Activities:")
for activity in selected_activities:
print(f"Activity {activity[2]} (Start Time: {activity[0]}, Finish Time: {activity[1]})")

53
OUTPUT:

Selected Activities:
Activity 3 (Start Time: 0, Finish Time: 6)
Activity 1 (Start Time: 1, Finish Time: 2)
Activity 4 (Start Time: 5, Finish Time: 7)
Activity 5 (Start Time: 5, Finish Time: 9)

RESULT:

Thus the python program to implement activity selection was executed and verified
successfully

54
Ex.No: 12b
HUFFMAN CODING
Date:

AIM:

To write a python program to implement Huffman Coding

ALGORITHM:

• Sort the message ensemble by decreasing probability.


• N is the cardinal of the message ensemble (number of different messages).
• Compute the integer n_0 such as 2<=n_0<=D and (N-n_0)/(D-1) is integer.
• Select the n_0 least probable messages, and assign them each a digit code.
• Substitute the selected messages by a composite message summing their
probability, and
• re-order it.
• While there remains more than one message, do steps thru 8.
• Select D least probable messages, and assign them each a digit code.
• Substitute the selected messages by a composite message summing their
probability, and
• re-order it.
• The code of each message is given by the concatenation of the code digits of the
aggregate they've been put in.

55
PROGRAM:

import heapq
from collections import defaultdict

class HuffmanNode:
def __init__(self, char, freq):
self.char = char
self.freq = freq
self.left = None
self.right = None

def __lt__(self, other):


return self.freq < other.freq

def build_huffman_tree(freq_map):
priority_queue = [HuffmanNode(char, freq) for char, freq in freq_map.items()]
heapq.heapify(priority_queue)

while len(priority_queue) > 1:


left_node = heapq.heappop(priority_queue)
right_node = heapq.heappop(priority_queue)

internal_node = HuffmanNode(None, left_node.freq + right_node.freq)


internal_node.left = left_node
internal_node.right = right_node

heapq.heappush(priority_queue, internal_node)

return priority_queue[0]

def build_huffman_codes(node, code="", mapping=None):


if mapping is None:
mapping = {}

if node.char is not None:


mapping[node.char] = code
if node.left is not None:
build_huffman_codes(node.left, code + "0", mapping)
if node.right is not None:
build_huffman_codes(node.right, code + "1", mapping)

return mapping

def huffman_coding(text):
freq_map = defaultdict(int)
for char in text:
freq_map[char] += 1

56
root = build_huffman_tree(freq_map)
codes = build_huffman_codes(root)

encoded_text = "".join(codes[char] for char in text)


return encoded_text, codes

# Example Usage and Output for Lab Practice


text_to_encode = "huffman coding is fun"
encoded_text, huffman_codes = huffman_coding(text_to_encode)

print("Original Text:", text_to_encode)


print("Encoded Text:", encoded_text)
print("Huffman Codes:")
for char, code in huffman_codes.items():
print(f"{char}: {code}")

57
OUTPUT

Original Text: huffman coding is fun


Encoded Text:
0101011101111111110110101101100011001111100011011010010111101110001110100
1111001010101101
Huffman Codes:
h: 010
u: 11000
f: 011
m: 1111
a: 00
n: 11001
c: 1110
o: 1101
d: 100
i: 0100
g: 11101
s: 1000
: 1010
1: 10110
1: 10111
1: 101110
0: 101111

RESULT:

Thus the python program to implement Huffman Coding was written, executed and verified
successfully.

58
59

You might also like