[Go to site: main page, start]

0% found this document useful (0 votes)
6 views10 pages

Python Sorting and Graph Algorithms

Uploaded by

25003
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)
6 views10 pages

Python Sorting and Graph Algorithms

Uploaded by

25003
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

Algorithm Lab Programs in Python

1. Insertion Sort (with Number of Comparisons)


def insertion_sort(arr):
"""Performs insertion sort and returns (sorted_list, comparison_count)."""
a = arr[:] # work on a copy
n = len(a)
comps = 0
for i in range(1, n):
key = a[i]
j=i-1
# Count every comparison between a[j] and key, including the final failure
while j >= 0:
comps += 1
if a[j] > key:
a[j + 1] = a[j]
j -= 1
else:
break
a[j + 1] = key
return a, comps

2. Merge Sort (with Number of Comparisons)


def _merge(left, right):
"""Merge two sorted lists and return (merged, comparison_count)."""
i=j=0
merged = []
comps = 0
while i < len(left) and j < len(right):
comps += 1
if left[i] <= right[j]:
[Link](left[i])
i += 1
else:
[Link](right[j])
j += 1
# Remaining elements (no comparisons)
[Link](left[i:])
[Link](right[j:])
return merged, comps
def merge_sort(arr):
"""Performs merge sort and returns (sorted_list, comparison_count)."""
n = len(arr)
if n <= 1:
return arr[:], 0
mid = n // 2
left, c1 = merge_sort(arr[:mid])
right, c2 = merge_sort(arr[mid:])
merged, c3 = _merge(left, right)
return merged, c1 + c2 + c3

3. Heap Sort (with Number of Comparisons)


def _heapify(a, n, i):
"""Heapify subtree rooted at index i; returns comparison_count."""
comps = 0
largest = i
left = 2 * i + 1
right = 2 * i + 2

if left < n:
comps += 1
if a[left] > a[largest]:
largest = left

if right < n:
comps += 1
if a[right] > a[largest]:
largest = right

if largest != i:
a[i], a[largest] = a[largest], a[i]
comps += _heapify(a, n, largest)

return comps

def heap_sort(arr):
"""Performs heap sort and returns (sorted_list, comparison_count)."""
a = arr[:]
n = len(a)
comps = 0
# Build max-heap
for i in range(n // 2 - 1, -1, -1):
comps += _heapify(a, n, i)

# Extract elements from heap


for i in range(n - 1, 0, -1):
a[0], a[i] = a[i], a[0]
comps += _heapify(a, i, 0)

return a, comps

4. Quick Sort (with Number of Comparisons)


def _partition(a, low, high):
"""Lomuto partition; returns (partition_index, comparison_count)."""
pivot = a[high]
i = low - 1
comps = 0
for j in range(low, high):
comps += 1
if a[j] <= pivot:
i += 1
a[i], a[j] = a[j], a[i]
a[i + 1], a[high] = a[high], a[i + 1]
return i + 1, comps

def _quick_sort_rec(a, low, high):


if low >= high:
return 0
pi, c1 = _partition(a, low, high)
c2 = _quick_sort_rec(a, low, pi - 1)
c3 = _quick_sort_rec(a, pi + 1, high)
return c1 + c2 + c3

def quick_sort(arr):
"""Performs quick sort and returns (sorted_list, comparison_count)."""
a = arr[:]
comps = _quick_sort_rec(a, 0, len(a) - 1)
return a, comps
Experimental Driver for Algorithms 1–4
# Experimental comparison of average number of comparisons
import random
import math
import [Link] as plt

def random_array(n, low=0, high=100000):


return [[Link](low, high) for _ in range(n)]

def run_experiments():
sizes = list(range(30, 1001, 10))
avg_ins, avg_merge, avg_heap, avg_quick, theo_nlogn = [], [], [], [], []

for n in sizes:
total_ins = total_merge = total_heap = total_quick = 0
for _ in range(10):
arr = random_array(n)
_, c_ins = insertion_sort(arr)
_, c_merge = merge_sort(arr)
_, c_heap = heap_sort(arr)
_, c_quick = quick_sort(arr)
total_ins += c_ins
total_merge += c_merge
total_heap += c_heap
total_quick += c_quick

avg_ins.append(total_ins / 10.0)
avg_merge.append(total_merge / 10.0)
avg_heap.append(total_heap / 10.0)
avg_quick.append(total_quick / 10.0)
theo_nlogn.append(n * math.log2(n))

# Plotting
[Link]()
[Link](sizes, avg_ins, label='Insertion Sort')
[Link](sizes, avg_merge, label='Merge Sort')
[Link](sizes, avg_heap, label='Heap Sort')
[Link](sizes, avg_quick, label='Quick Sort')
[Link](sizes, theo_nlogn, label='n log n (Theoretical)', linestyle='--')
[Link]('Input Size (n)')
[Link]('Average Number of Comparisons')
[Link]('Average Comparisons vs Input Size')
[Link]()
[Link](True)
[Link]()

# To run the experiment and plot graphs:


# run_experiments()

5. Strassen’s Algorithm for Matrix Multiplication


def add_matrix(A, B):
n = len(A)
return [[A[i][j] + B[i][j] for j in range(n)] for i in range(n)]

def sub_matrix(A, B):


n = len(A)
return [[A[i][j] - B[i][j] for j in range(n)] for i in range(n)]

def strassen(A, B):


"""Strassen multiplication for square matrices of size power of 2."""
n = len(A)
if n == 1:
return [[A[0][0] * B[0][0]]]

k = n // 2
# Split A
A11 = [[A[i][j] for j in range(k)] for i in range(k)]
A12 = [[A[i][j] for j in range(k, n)] for i in range(k)]
A21 = [[A[i][j] for j in range(k)] for i in range(k, n)]
A22 = [[A[i][j] for j in range(k, n)] for i in range(k, n)]
# Split B
B11 = [[B[i][j] for j in range(k)] for i in range(k)]
B12 = [[B[i][j] for j in range(k, n)] for i in range(k)]
B21 = [[B[i][j] for j in range(k)] for i in range(k, n)]
B22 = [[B[i][j] for j in range(k, n)] for i in range(k, n)]

M1 = strassen(add_matrix(A11, A22), add_matrix(B11, B22))


M2 = strassen(add_matrix(A21, A22), B11)
M3 = strassen(A11, sub_matrix(B12, B22))
M4 = strassen(A22, sub_matrix(B21, B11))
M5 = strassen(add_matrix(A11, A12), B22)
M6 = strassen(sub_matrix(A21, A11), add_matrix(B11, B12))
M7 = strassen(sub_matrix(A12, A22), add_matrix(B21, B22))
C11 = add_matrix(sub_matrix(add_matrix(M1, M4), M5), M7)
C12 = add_matrix(M3, M5)
C21 = add_matrix(M2, M4)
C22 = add_matrix(sub_matrix(add_matrix(M1, M3), M2), M6)

# Combine quadrants
C = [[0] * n for _ in range(n)]
for i in range(k):
for j in range(k):
C[i][j] = C11[i][j]
C[i][j + k] = C12[i][j]
C[i + k][j] = C21[i][j]
C[i + k][j + k] = C22[i][j]
return C

6. Counting Sort
def counting_sort(arr, max_val=None):
"""Counting sort for non-negative integers."""
if not arr:
return []

if max_val is None:
max_val = max(arr)

count = [0] * (max_val + 1)


for x in arr:
count[x] += 1

# Prefix sums (for stable sort optional)


for i in range(1, len(count)):
count[i] += count[i - 1]

output = [0] * len(arr)


# Stable version: traverse from right to left
for x in reversed(arr):
count[x] -= 1
output[count[x]] = x

return output
7. Breadth-First Search (BFS) on a Graph
from collections import deque

def bfs(graph, start):


"""
graph: adjacency list (dict or list of lists)
start: starting vertex
Returns the order of visited nodes.
"""
visited = set()
order = []
q = deque([start])
[Link](start)

while q:
u = [Link]()
[Link](u)
for v in graph[u]:
if v not in visited:
[Link](v)
[Link](v)
return order

# Example:
# graph = {
# 0: [1, 2],
# 1: [0, 3],
# 2: [0, 3],
# 3: [1, 2]
#}
# print(bfs(graph, 0))

8. Depth-First Search (DFS) on a Graph


def dfs(graph, start):
"""DFS using recursion; returns the order of visited nodes."""
visited = set()
order = []

def _dfs(u):
[Link](u)
[Link](u)
for v in graph[u]:
if v not in visited:
_dfs(v)

_dfs(start)
return order

# Example:
# graph = {
# 0: [1, 2],
# 1: [0, 3],
# 2: [0, 3],
# 3: [1, 2]
#}
# print(dfs(graph, 0))

9. Prim’s Algorithm for Minimum Spanning Tree


import math

def prim_mst(graph):
"""
graph: adjacency matrix; graph[u][v] = weight or 0/inf if no edge.
Returns parent array describing MST.
"""
n = len(graph)
key = [[Link]] * n
parent = [-1] * n
in_mst = [False] * n

key[0] = 0

for _ in range(n):
# Pick the minimum key vertex not yet included
u = -1
min_key = [Link]
for v in range(n):
if not in_mst[v] and key[v] < min_key:
min_key = key[v]
u=v

in_mst[u] = True

# Update key value and parent index of the adjacent vertices


for v in range(n):
w = graph[u][v]
if w != 0 and not in_mst[v] and w < key[v]:
key[v] = w
parent[v] = u

return parent

# After getting parent, MST edges are (parent[i], i) for i in 1..n-1

10. Dijkstra’s Algorithm (Single Source Shortest Path)


import heapq

def dijkstra(graph, source):


"""
graph: adjacency list, e.g. graph[u] = [(v, weight), ...]
source: starting vertex
Returns distances array.
"""
n = len(graph)
dist = [[Link]] * n
dist[source] = 0
pq = [(0, source)] # (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 = {
# 0: [(1, 4), (2, 1)],
# 1: [(3, 1)],
# 2: [(1, 2), (3, 5)],
# 3: []
#}
# dist = dijkstra(graph, 0)
11. 0-1 Knapsack Problem using Dynamic Programming
def knapsack_01(weights, values, W):
"""
weights: list of item weights
values: corresponding list of item values
W: capacity of knapsack
Returns maximum value achievable.
"""
n = len(weights)
# dp[i][w] = best value using first i items with capacity w
dp = [[0] * (W + 1) for _ in range(n + 1)]

for i in range(1, n + 1):


wt = weights[i - 1]
val = values[i - 1]
for w in range(W + 1):
if wt <= w:
dp[i][w] = max(dp[i - 1][w], val + dp[i - 1][w - wt])
else:
dp[i][w] = dp[i - 1][w]

return dp[n][W]

# Example:
# weights = [2, 3, 4, 5]
# values = [3, 4, 5, 6]
#W=5
# print(knapsack_01(weights, values, W)) # Output: 7

You might also like