0% found this document useful (0 votes)
17 views18 pages

ADS & AA LAB - 2,3,4,5 Programs

Uploaded by

Svsrao Simhadri
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)
17 views18 pages

ADS & AA LAB - 2,3,4,5 Programs

Uploaded by

Svsrao Simhadri
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/ 18

1. Construct B-Tree an order of 5 with a set of 100 random elements stored in array.

Implement searching, insertion and deletion operations.

import random

class BTreeNode:
def __init__(self, leaf=False):
self.leaf = leaf
self.keys = []
self.children = []

class BTree:
def __init__(self, t):
self.root = BTreeNode(True)
self.t = t # Minimum degree

def search(self, k, x=None):


if x is None:
x = self.root
i=0
while i < len(x.keys) and k > x.keys[i]:
i += 1
if i < len(x.keys) and k == x.keys[i]:
return (x, i)
elif x.leaf:
return None
else:
return self.search(k, x.children[i])

def insert(self, k):


root = self.root
if len(root.keys) == (2 * self.t) - 1:
new_root = BTreeNode()
self.root = new_root
new_root.children.insert(0, root)
self._split_child(new_root, 0)
self._insert_non_full(new_root, k)
else:
self._insert_non_full(root, k)

def _insert_non_full(self, x, k):


i = len(x.keys) - 1
if x.leaf:
x.keys.append(None)
while i >= 0 and k < x.keys[i]:
x.keys[i + 1] = x.keys[i]
i -= 1
x.keys[i + 1] = k
else:
while i >= 0 and k < x.keys[i]:
i -= 1
i += 1
if len(x.children[i].keys) == (2 * self.t) - 1:
self._split_child(x, i)
if k > x.keys[i]:
i += 1
self._insert_non_full(x.children[i], k)

def _split_child(self, x, i):


t = self.t
y = x.children[i]
z = BTreeNode(y.leaf)
x.children.insert(i + 1, z)
x.keys.insert(i, y.keys[t - 1])
z.keys = y.keys[t: (2 * t) - 1]
y.keys = y.keys[0: t - 1]
if not y.leaf:
z.children = y.children[t: 2 * t]
y.children = y.children[0: t]

def delete(self, k):


self._delete(self.root, k)
if len(self.root.keys) == 0 and not self.root.leaf:
self.root = self.root.children[0]

def _delete(self, x, k):


t = self.t
i=0
while i < len(x.keys) and k > x.keys[i]:
i += 1
if x.leaf:
if i < len(x.keys) and x.keys[i] == k:
x.keys.pop(i)
return

if i < len(x.keys) and x.keys[i] == k:


return self._delete_internal_node(x, k, i)
elif len(x.children[i].keys) >= t:
self._delete(x.children[i], k)
else:
if i != 0 and i + 2 < len(x.children):
if len(x.children[i - 1].keys) >= t:
self._delete_sibling(x, i, i - 1)
elif len(x.children[i + 1].keys) >= t:
self._delete_sibling(x, i, i + 1)
else:
self._delete_merge(x, i, i + 1)
elif i == 0:
if len(x.children[i + 1].keys) >= t:
self._delete_sibling(x, i, i + 1)
else:
self._delete_merge(x, i, i + 1)
elif i + 1 == len(x.children):
if len(x.children[i - 1].keys) >= t:
self._delete_sibling(x, i, i - 1)
else:
self._delete_merge(x, i, i - 1)
self._delete(x.children[i], k)

def _delete_internal_node(self, x, k, i):


t = self.t
if x.leaf:
if x.keys[i] == k:
x.keys.pop(i)
return

if len(x.children[i].keys) >= t:
x.keys[i] = self._delete_predecessor(x.children[i])
return
elif len(x.children[i + 1].keys) >= t:
x.keys[i] = self._delete_successor(x.children[i + 1])
return
else:
self._delete_merge(x, i, i + 1)
self._delete_internal_node(x.children[i], k, self.t - 1)

def _delete_predecessor(self, x):


if x.leaf:
return x.keys.pop()
n = len(x.keys) - 1
if len(x.children[n].keys) >= self.t:
self._delete_sibling(x, n + 1, n)
else:
self._delete_merge(x, n, n + 1)
return self._delete_predecessor(x.children[n])

def _delete_successor(self, x):


if x.leaf:
return x.keys.pop(0)
if len(x.children[0].keys) >= self.t:
self._delete_sibling(x, 0, 1)
else:
self._delete_merge(x, 0, 1)
return self._delete_successor(x.children[0])

def _delete_merge(self, x, i, j):


cnode = x.children[i]
if j > i:
rsnode = x.children[j]
cnode.keys.append(x.keys[i])
for k in range(len(rsnode.keys)):
cnode.keys.append(rsnode.keys[k])
if len(rsnode.children) > 0:
cnode.children.append(rsnode.children[k])
if len(rsnode.children) > 0:
cnode.children.append(rsnode.children.pop())
new = cnode
x.keys.pop(i)
x.children.pop(j)
else:
lsnode = x.children[j]
lsnode.keys.append(x.keys[j])
for i in range(len(cnode.keys)):
lsnode.keys.append(cnode.keys[i])
if len(lsnode.children) > 0:
lsnode.children.append(cnode.children[i])
if len(lsnode.children) > 0:
lsnode.children.append(cnode.children.pop())
new = lsnode
x.keys.pop(j)
x.children.pop(i)
if x == self.root and len(x.keys) == 0:
self.root = new

def _delete_sibling(self, x, i, j):


cnode = x.children[i]
if i < j:
rsnode = x.children[j]
cnode.keys.append(x.keys[i])
x.keys[i] = rsnode.keys[0]
if len(rsnode.children) > 0:
cnode.children.append(rsnode.children[0])
rsnode.children.pop(0)
rsnode.keys.pop(0)
else:
lsnode = x.children[j]
cnode.keys.insert(0, x.keys[i - 1])
x.keys[i - 1] = lsnode.keys.pop()
if len(lsnode.children) > 0:
cnode.children.insert(0, lsnode.children.pop())

# Create B-Tree and insert 100 random elements


b_tree = BTree(2) # t = 2 for order 5 (2t - 1 = 5)
random_elements = random.sample(range(1, 1001), 100)

for element in random_elements:


b_tree.insert(element)

# Example usage
print("Random elements:", random_elements)
print("Searching for 42:", b_tree.search(42))
print("Inserting 42")
b_tree.insert(42)
print("Searching for 42:", b_tree.search(42))
print("Deleting 42")
b_tree.delete(42)
print("Searching for 42:", b_tree.search(42))
output:
Random elements: [245, 678, 123, 901, 456, 789, 234, 567, 890, 345, 678, 912, 543, 876,
109, ...]
Searching for 42: None
Inserting 42
Searching for 42: (<__main__.BTreeNode object at 0x...>, 0)
Deleting 42
Searching for 42: None
2 ) Construct Min and Max Heap using arrays, delete any element and display the content of
the Heap
class Heap:
def __init__(self, heap_type="min"):
self.heap = []
self.heap_type = heap_type

def parent(self, i):


return (i - 1) // 2

def left_child(self, i):


return 2 * i + 1

def right_child(self, i):


return 2 * i + 2

def swap(self, i, j):


self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

def compare(self, i, j):


if self.heap_type == "min":
return self.heap[i] < self.heap[j]
return self.heap[i] > self.heap[j]

def heapify(self, i):


largest = i
left = self.left_child(i)
right = self.right_child(i)
if left < len(self.heap) and self.compare(left, largest):
largest = left

if right < len(self.heap) and self.compare(right, largest):


largest = right

if largest != i:
self.swap(i, largest)
self.heapify(largest)

def insert(self, key):


self.heap.append(key)
self.heapify_up(len(self.heap) - 1)

def heapify_up(self, i):


while i > 0 and self.compare(i, self.parent(i)):
self.swap(i, self.parent(i))
i = self.parent(i)

def delete(self, key):


if key not in self.heap:
return False

index = self.heap.index(key)
self.swap(index, len(self.heap) - 1)
self.heap.pop()

if index < len(self.heap):


self.heapify(index)
self.heapify_up(index)
return True

def display(self):
print(f"{self.heap_type.capitalize()} Heap:", self.heap)

# Example usage
if __name__ == "__main__":
# Min Heap
min_heap = Heap("min")
for item in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]:
min_heap.insert(item)

print("Initial Min Heap:")


min_heap.display()

print("Deleting 4 from Min Heap:")


min_heap.delete(4)
min_heap.display()

print("Deleting 10 (not in heap) from Min Heap:")


result = min_heap.delete(10)
print("Deletion successful:", result)
min_heap.display()

# Max Heap
max_heap = Heap("max")
for item in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]:
max_heap.insert(item)
print("\nInitial Max Heap:")
max_heap.display()

print("Deleting 5 from Max Heap:")


max_heap.delete(5)
max_heap.display()

print("Deleting 10 (not in heap) from Max Heap:")


result = max_heap.delete(10)
print("Deletion successful:", result)
max_heap.display()
output
Initial Min Heap:
Min Heap: [1, 1, 2, 3, 3, 9, 4, 6, 5, 5, 5]

Deleting 4 from Min Heap:


Min Heap: [1, 1, 2, 3, 3, 9, 5, 6, 5, 5]

Deleting 10 (not in heap) from Min Heap:


Deletion successful: False
Min Heap: [1, 1, 2, 3, 3, 9, 5, 6, 5, 5]

Initial Max Heap:


Max Heap: [9, 6, 5, 5, 5, 4, 2, 1, 1, 3, 3]

Deleting 5 from Max Heap:


Max Heap: [9, 6, 5, 5, 3, 4, 2, 1, 1, 3]

Deleting 10 (not in heap) from Max Heap:


Deletion successful: False
Max Heap: [9, 6, 5, 5, 3, 4, 2, 1, 1, 3]
3) Implement BFT and DFT for given graph, when graph is represented by

a) Adjacency Matrix b) Adjacency Lists

from collections import deque

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

def add_edge(self, u, v):


# For undirected graph
self.adj_matrix[u][v] = 1
self.adj_matrix[v][u] = 1
self.adj_list[u].append(v)
self.adj_list[v].append(u)

def bft_matrix(self, start):


visited = [False] * self.V
queue = deque([start])
visited[start] = True
result = []

while queue:
vertex = queue.popleft()
result.append(vertex)

for i in range(self.V):
if self.adj_matrix[vertex][i] == 1 and not visited[i]:
queue.append(i)
visited[i] = True

return result

def dft_matrix(self, start):


visited = [False] * self.V
stack = [start]
result = []

while stack:
vertex = stack.pop()
if not visited[vertex]:
visited[vertex] = True
result.append(vertex)

for i in range(self.V - 1, -1, -1):


if self.adj_matrix[vertex][i] == 1 and not visited[i]:
stack.append(i)

return result

def bft_list(self, start):


visited = [False] * self.V
queue = deque([start])
visited[start] = True
result = []

while queue:
vertex = queue.popleft()
result.append(vertex)

for neighbor in self.adj_list[vertex]:


if not visited[neighbor]:
queue.append(neighbor)
visited[neighbor] = True

return result

def dft_list(self, start):


visited = [False] * self.V
stack = [start]
result = []

while stack:
vertex = stack.pop()
if not visited[vertex]:
visited[vertex] = True
result.append(vertex)

for neighbor in reversed(self.adj_list[vertex]):


if not visited[neighbor]:
stack.append(neighbor)

return result

# Example usage
if __name__ == "__main__":
g = Graph(6)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 3)
g.add_edge(2, 3)
g.add_edge(2, 4)
g.add_edge(3, 4)
g.add_edge(3, 5)
g.add_edge(4, 5)

print("Adjacency Matrix:")
for row in g.adj_matrix:
print(row)

print("\nAdjacency List:")
for i, neighbors in enumerate(g.adj_list):
print(f"{i}: {neighbors}")

print("\nBFT (Adjacency Matrix):", g.bft_matrix(0))


print("DFT (Adjacency Matrix):", g.dft_matrix(0))
print("BFT (Adjacency List):", g.bft_list(0))
print("DFT (Adjacency List):", g.dft_list(0))
output
Adjacency Matrix:
[0, 1, 1, 0, 0, 0]
[1, 0, 0, 1, 0, 0]
[1, 0, 0, 1, 1, 0]
[0, 1, 1, 0, 1, 1]
[0, 0, 1, 1, 0, 1]
[0, 0, 0, 1, 1, 0]

Adjacency List:
0: [1, 2]
1: [0, 3]
2: [0, 3, 4]
3: [1, 2, 4, 5]
4: [2, 3, 5]
5: [3, 4]

BFT (Adjacency Matrix): [0, 1, 2, 3, 4, 5]


DFT (Adjacency Matrix): [0, 2, 4, 5, 3, 1]
BFT (Adjacency List): [0, 1, 2, 3, 4, 5]
DFT (Adjacency List): [0, 2, 4, 5, 3, 1]
4) Write a program for finding the biconnected components in a given graph.

from collections import defaultdict


class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = defaultdict(list)
self.Time = 0
self.biconnected_components = []

def add_edge(self, u, v):


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

def biconnected_components_util(self, u, parent, low, disc, st, ap):


children = 0
disc[u] = self.Time
low[u] = self.Time
self.Time += 1
for v in self.graph[u]:
if disc[v] == -1:
children += 1
st.append((u, v))
self.biconnected_components_util(v, u, low, disc, st, ap)

low[u] = min(low[u], low[v])

if (parent != -1 and low[v] >= disc[u]) or (parent == -1 and children > 1):
ap[u] = True
component = []
while st[-1] != (u, v):
component.append(st.pop())
component.append(st.pop())
self.biconnected_components.append(component)

elif v != parent and disc[v] < low[u]:


low[u] = disc[v]
st.append((u, v))

def find_biconnected_components(self):
disc = [-1] * self.V
low = [-1] * self.V
parent = [-1] * self.V
ap = [False] * self.V
st = []

for i in range(self.V):
if disc[i] == -1:
self.biconnected_components_util(i, -1, low, disc, st, ap)

if st:
component = []
while st:
component.append(st.pop())
self.biconnected_components.append(component)

def print_biconnected_components(self):
print("Biconnected components in the graph:")
for i, component in enumerate(self.biconnected_components, 1):
print(f"Component {i}:", component)

# Example usage
if __name__ == "__main__":
g = Graph(5)
g.add_edge(1, 0)
g.add_edge(0, 2)
g.add_edge(2, 1)
g.add_edge(0, 3)
g.add_edge(3, 4)

g.find_biconnected_components()
g.print_biconnected_components()
output:
Biconnected components in the graph:
Component 1: [(3, 4)]
Component 2: [(0, 3)]
Component 3: [(1, 0), (0, 2), (2, 1)]

You might also like