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

Ads Lab Manual

The document outlines the implementation of a B-Tree of order 5, including operations for searching, insertion, and deletion of elements. It also describes the construction of Min and Max Heaps using arrays, detailing their respective insertion, deletion, and display functionalities. Additionally, it mentions the implementation of Breadth-First Traversal (BFT) and Depth-First Traversal (DFT) for graphs represented by adjacency matrices.

Uploaded by

vydaprasanna
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 views23 pages

Ads Lab Manual

The document outlines the implementation of a B-Tree of order 5, including operations for searching, insertion, and deletion of elements. It also describes the construction of Min and Max Heaps using arrays, detailing their respective insertion, deletion, and display functionalities. Additionally, it mentions the implementation of Breadth-First Traversal (BFT) and Depth-First Traversal (DFT) for graphs represented by adjacency matrices.

Uploaded by

vydaprasanna
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
You are on page 1/ 23

2.

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, t, leaf=False):

self.t = t

self.leaf = leaf

self.keys = []

self.children = []

class BTree:

def __init__(self, t):

self.t = t

self.root = BTreeNode(t, True)

def traverse(self, node, level=0):

if node is not None:

print("Level", level, ":", node.keys)

level += 1

if not node.leaf:

for child in node.children:

self.traverse(child, level)

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

if node is None:

node = self.root
i=0

while i < len(node.keys) and k > node.keys[i]:

i += 1

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

return node

if node.leaf:

return None

return self.search(k, node.children[i])

def insert(self, k):

root = self.root

if len(root.keys) == 2 * self.t - 1:

s = BTreeNode(self.t, False)

self.root = s

s.children.append(root)

self.split_child(s, 0)

self.insert_non_full(s, k)

else:

self.insert_non_full(root, k)

def insert_non_full(self, node, k):

i = len(node.keys) - 1

if node.leaf:

node.keys.append(None)

while i >= 0 and k < node.keys[i]:

node.keys[i + 1] = node.keys[i]

i -= 1

node.keys[i + 1] = k

else:
while i >= 0 and k < node.keys[i]:

i -= 1

i += 1

if len(node.children[i].keys) == 2 * self.t - 1:

self.split_child(node, i)

if k > node.keys[i]:

i += 1

self.insert_non_full(node.children[i], k)

def split_child(self, parent, i):

t = self.t

y = parent.children[i]

z = BTreeNode(t, y.leaf)

parent.children.insert(i + 1, z)

parent.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, node, k):

t = self.t

idx = self._find_key(node, k)
if idx < len(node.keys) and node.keys[idx] == k:

if node.leaf:

node.keys.pop(idx)

else:

self._delete_internal(node, idx)

else:

if node.leaf:

return

flag = (idx == len(node.keys))

if len(node.children[idx].keys) < t:

self._fill(node, idx)

if flag and idx > len(node.keys):

self._delete(node.children[idx - 1], k)

else:

self._delete(node.children[idx], k)

def _find_key(self, node, k):

idx = 0

while idx < len(node.keys) and node.keys[idx] < k:

idx += 1

return idx

def _delete_internal(self, node, idx):

t = self.t

k = node.keys[idx]

if len(node.children[idx].keys) >= t:

pred = self._get_predecessor(node, idx)

node.keys[idx] = pred

self._delete(node.children[idx], pred)
elif len(node.children[idx + 1].keys) >= t:

succ = self._get_successor(node, idx)

node.keys[idx] = succ

self._delete(node.children[idx + 1], succ)

else:

self._merge(node, idx)

self._delete(node.children[idx], k)

def _get_predecessor(self, node, idx):

cur = node.children[idx]

while not cur.leaf:

cur = cur.children[-1]

return cur.keys[-1]

def _get_successor(self, node, idx):

cur = node.children[idx + 1]

while not cur.leaf:

cur = cur.children[0]

return cur.keys[0]

def _fill(self, node, idx):

t = self.t

if idx != 0 and len(node.children[idx - 1].keys) >= t:

self._borrow_from_prev(node, idx)

elif idx != len(node.keys) and len(node.children[idx + 1].keys) >= t:

self._borrow_from_next(node, idx)

else:

if idx != len(node.keys):

self._merge(node, idx)
else:

self._merge(node, idx - 1)

def _borrow_from_prev(self, node, idx):

child = node.children[idx]

sibling = node.children[idx - 1]

child.keys.insert(0, node.keys[idx - 1])

if not child.leaf:

child.children.insert(0, sibling.children.pop())

node.keys[idx - 1] = sibling.keys.pop()

def _borrow_from_next(self, node, idx):

child = node.children[idx]

sibling = node.children[idx + 1]

child.keys.append(node.keys[idx])

if not sibling.leaf:

child.children.append(sibling.children.pop(0))

node.keys[idx] = sibling.keys.pop(0)

def _merge(self, node, idx):

child = node.children[idx]

sibling = node.children[idx + 1]

child.keys.append(node.keys[idx])

child.keys.extend(sibling.keys)

if not child.leaf:

child.children.extend(sibling.children)

node.keys.pop(idx)

node.children.pop(idx + 1)
# Helper function to create random elements and test B-Tree

def test_btree():

t = 5 # Order of B-Tree

btree = BTree(t)

# Generate 100 random elements

elements = random.sample(range(1, 1000), 100)

print("Inserting elements:")

for el in elements:

btree.insert(el)

print(f"Inserted {el}")

print("\nTraversing B-Tree:")

btree.traverse(btree.root)

search_value = random.choice(elements)

print(f"\nSearching for {search_value}:")

result = btree.search(search_value)

if result:

print(f"Element {search_value} found.")

else:

print(f"Element {search_value} not found.")

delete_value = random.choice(elements)

print(f"\nDeleting {delete_value}:")

btree.delete(delete_value)

print(f"Deleted {delete_value}.")
print("\nTraversing B-Tree after deletion:")

btree.traverse(btree.root)

if __name__ == "__main__":

test_btree()

OUTPUT:

Inserting elements:

Inserted 940

Inserted 620

Inserted 2

Inserted 175

Inserted 365

Inserted 367

Inserted 380

Inserted 315

Inserted 674

Inserted 833

Inserted 855

Inserted 188

Inserted 437

Inserted 245

Inserted 484

Inserted 482

Inserted 941

Inserted 737

Inserted 130

Inserted 336

Inserted 578

Inserted 707
Traversing B-Tree:

Level 0 : [484]

Level 1 : [130, 164, 188, 279, 367, 424]

Level 2 : [2, 11, 32, 40, 54, 67, 73, 84, 102]

Level 2 : [136, 148, 152, 160, 163]

Searching for 367:

Element 367 found.

Deleting 279:

Deleted 279.

Traversing B-Tree after deletion:

Level 0 : [484]

Level 1 : [130, 164, 188, 271, 367, 424]

Level 2 : [2, 11, 32, 40, 54, 67, 73, 84, 102]

Level 2 : [136, 148, 152, 160, 163]

Level 2 : [836, 848, 855, 859, 865, 868, 889, 908, 922]

Level 2 : [940, 941, 943, 963, 964, 973]

3.Construct Min and Max Heap using arrays, delete any element and display the
content of the Heap.

MIN HEAP

To implement a Min Heap using arrays in Python, you need to manage heap operations such as
insertion, deletion, and displaying the heap. The Min Heap is a binary heap where the smallest
element is at the root, and each parent node is less than or equal to its child nodes.

Here’s a detailed Python program that includes:


1. Inserting elements into the Min Heap.
2. Deleting the minimum element (root).
3. Deleting any arbitrary element.
4. Displaying the content of the heap.

class MinHeap:
def __init__(self):
self.heap = []

def parent(self, index):


return (index - 1) // 2

def left_child(self, index):


return 2 * index + 1

def right_child(self, index):


return 2 * index + 2

def insert(self, key):


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

def _heapify_up(self, index):


parent = self.parent(index)
if index > 0 and self.heap[index] < self.heap[parent]:
self.heap[index], self.heap[parent] = self.heap[parent], self.heap[index]
self._heapify_up(parent)

def extract_min(self):
if len(self.heap) == 0:
raise IndexError("Heap is empty")
min_value = self.heap[0]
last_value = self.heap.pop()
if len(self.heap) > 0:
self.heap[0] = last_value
self._heapify_down(0)
return min_value

def _heapify_down(self, index):


smallest = index
left = self.left_child(index)
right = self.right_child(index)
if left < len(self.heap) and self.heap[left] < self.heap[smallest]:
smallest = left

if right < len(self.heap) and self.heap[right] < self.heap[smallest]:


smallest = right

if smallest != index:
self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
self._heapify_down(smallest)

def delete(self, key):


if len(self.heap) == 0:
raise IndexError("Heap is empty")

# Find the index of the element to be deleted


try:
index = self.heap.index(key)
except ValueError:
raise ValueError("Element not found in the heap")

# Replace it with the last element and remove the last element
self.heap[index] = self.heap.pop()

# Restore the heap property


if index < len(self.heap):
self._heapify_down(index)
self._heapify_up(index)

def display(self):
print("Min Heap:", self.heap)

def main():
min_heap = MinHeap()

# Insert elements into the Min Heap


min_heap.insert(10)
min_heap.insert(5)
min_heap.insert(15)
min_heap.insert(2)
min_heap.insert(8)
print("Min Heap after inserts:")
min_heap.display()

# Extract the minimum element


print("Extracted Min:", min_heap.extract_min())
print("Min Heap after extracting min:")
min_heap.display()

# Delete a specific element


min_heap.delete(15)
print("Min Heap after deleting element 15:")
min_heap.display()

if __name__ == "__main__":
main()

OUTPUT

Min Heap after inserts:


Min Heap: [2, 5, 15, 10, 8]
Extracted Min: 2
Min Heap after extracting min:
Min Heap: [5, 8, 15, 10]
Min Heap after deleting element 15:
Min Heap: [5, 8, 10]

MAX HEAP

To implement a Max Heap using arrays in Python, you need to handle operations such as
insertion, deletion of arbitrary elements, and displaying the heap contents. A Max Heap is a
binary heap where each parent node is greater than or equal to its child nodes, and the largest
element is at the root.

Here's a Python program that covers:

1. Inserting elements into the Max Heap.


2. Deleting any arbitrary element.
3. Displaying the content of the heap.

class MaxHeap:
def __init__(self):

self.heap = []

def parent(self, index):

return (index - 1) // 2

def left_child(self, index):

return 2 * index + 1

def right_child(self, index):

return 2 * index + 2

def insert(self, key):

self.heap.append(key)

self._heapify_up(len(self.heap) - 1)

def _heapify_up(self, index):

parent = self.parent(index)

if index > 0 and self.heap[index] > self.heap[parent]:

self.heap[index], self.heap[parent] = self.heap[parent], self.heap[index]

self._heapify_up(parent)

def extract_max(self):

if len(self.heap) == 0:

raise IndexError("Heap is empty")

max_value = self.heap[0]

last_value = self.heap.pop()

if len(self.heap) > 0:

self.heap[0] = last_value
self._heapify_down(0)

return max_value

def _heapify_down(self, index):

largest = index

left = self.left_child(index)

right = self.right_child(index)

if left < len(self.heap) and self.heap[left] > self.heap[largest]:

largest = left

if right < len(self.heap) and self.heap[right] > self.heap[largest]:

largest = right

if largest != index:

self.heap[index], self.heap[largest] = self.heap[largest], self.heap[index]

self._heapify_down(largest)

def delete(self, key):

if len(self.heap) == 0:

raise IndexError("Heap is empty")

# Find the index of the element to be deleted

try:

index = self.heap.index(key)

except ValueError:

raise ValueError("Element not found in the heap")

# Replace it with the last element and remove the last element
self.heap[index] = self.heap.pop()

# Restore the heap property

if index < len(self.heap):

self._heapify_down(index)

self._heapify_up(index)

def display(self):

print("Max Heap:", self.heap)

def main():

max_heap = MaxHeap()

# Insert elements into the Max Heap

max_heap.insert(10)

max_heap.insert(5)

max_heap.insert(15)

max_heap.insert(20)

max_heap.insert(8)

print("Max Heap after inserts:")

max_heap.display()

# Extract the maximum element

print("Extracted Max:", max_heap.extract_max())

print("Max Heap after extracting max:")

max_heap.display()

# Delete a specific element


max_heap.delete(15)

print("Max Heap after deleting element 15:")

max_heap.display()

if __name__ == "__main__":

main()

OUTPUT

Max Heap after inserts:

Max Heap: [20, 15, 10, 5, 8]

Extracted Max: 20

Max Heap after extracting max:

Max Heap: [15, 8, 10, 5]

Max Heap after deleting element 15:

Max Heap: [10, 8, 5]

4. Implement BFT and DFT for given graph, when graph is represented by
Adjacency Matrix

Adjacency Lists

Adjacency Matrix

To implement Breadth-First Traversal (BFT) and Depth-First Traversal (DFT) for a graph
represented by an adjacency matrix, we'll create a Python program that includes the following
steps:

1. Represent the Graph: Use an adjacency matrix to represent the graph.


2. Implement Breadth-First Traversal (BFT): Traverse the graph level by level using a
queue.
3. Implement Depth-First Traversal (DFT): Traverse the graph deeply using a stack (or
recursive approach).
from collections import deque

class Graph:

def __init__(self, adjacency_matrix):

self.adj_matrix = adjacency_matrix

self.num_vertices = len(adjacency_matrix)

def bfs(self, start_vertex):

visited = [False] * self.num_vertices

queue = deque([start_vertex])

visited[start_vertex] = True

result = []

while queue:

vertex = queue.popleft()

result.append(vertex)

for i in range(self.num_vertices):

if self.adj_matrix[vertex][i] == 1 and not visited[i]:

queue.append(i)

visited[i] = True

return result

def dfs(self, start_vertex):

visited = [False] * self.num_vertices

result = []

self._dfs_util(start_vertex, visited, result)

return result
def _dfs_util(self, vertex, visited, result):

visited[vertex] = True

result.append(vertex)

for i in range(self.num_vertices):

if self.adj_matrix[vertex][i] == 1 and not visited[i]:

self._dfs_util(i, visited, result)

def main():

# Example adjacency matrix for a graph

adj_matrix = [

[0, 1, 1, 0],

[1, 0, 1, 1],

[1, 1, 0, 1],

[0, 1, 1, 0]

graph = Graph(adj_matrix)

start_vertex = 0

# Breadth-First Traversal

print("Breadth-First Traversal (BFT) starting from vertex", start_vertex, ":")

print(graph.bfs(start_vertex))

# Depth-First Traversal

print("Depth-First Traversal (DFT) starting from vertex", start_vertex, ":")

print(graph.dfs(start_vertex))

if __name__ == "__main__":
main()

OUTPUT

Breadth-First Traversal (BFT) starting from vertex 0 :

[0, 1, 2, 3]

Depth-First Traversal (DFT) starting from vertex 0 :

[0, 1, 2, 3]

ADJACENCY LIST

from collections import deque

class GraphList:

def __init__(self, adjacency_list):

self.adj_list = adjacency_list

self.num_vertices = len(adjacency_list)

def bfs(self, start_vertex):

visited = [False] * self.num_vertices

queue = deque([start_vertex])

visited[start_vertex] = 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 dfs(self, start_vertex):

visited = [False] * self.num_vertices

result = []

self._dfs_util(start_vertex, visited, result)

return result

def _dfs_util(self, vertex, visited, result):

visited[vertex] = True

result.append(vertex)

for neighbor in self.adj_list[vertex]:

if not visited[neighbor]:

self._dfs_util(neighbor, visited, result)

def main_list():

# Example adjacency list for a graph

adj_list = {

0: [1, 2],

1: [0, 2, 3],

2: [0, 1, 3],

3: [1, 2]

graph = GraphList(adj_list)
start_vertex = 0

# Breadth-First Traversal

print("Breadth-First Traversal (BFT) starting from vertex", start_vertex, ":")

print(graph.bfs(start_vertex))

# Depth-First Traversal

print("Depth-First Traversal (DFT) starting from vertex", start_vertex, ":")

print(graph.dfs(start_vertex))

if __name__ == "__main__":

main_list()

OUTPUT:

Breadth-First Traversal (BFT) starting from vertex 0 :

[0, 1, 2, 3]

Depth-First Traversal (DFT) starting from vertex 0 :

[0, 1, 2, 3]
5. Write a program for finding the biconnected components in a given graph

from collections import defaultdict, deque

class Graph:
def __init__(self, vertices):
self.vertices = vertices
self.adj_list = defaultdict(list)

def add_edge(self, u, v):


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

def biconnected_components(self):
# Initialize required variables
time = [0]
stack = []
bccs = []
visited = set()
disc = {}
low = {}
parent = {}

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

for v in self.adj_list[u]:
if v not in visited:
stack.append((u, v)) # Store the edge in stack
parent[v] = u
children += 1

dfs(v)

# Check if the subtree rooted at v has a connection back to one of ancestors of u


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

# If the lowest vertex reachable from subtree under v is below u in DFS tree, then u-v is a
bridge
if low[v] >= disc[u]:
bcc = []
while stack[-1] != (u, v):
bcc.append(stack.pop())
bcc.append(stack.pop())
bccs.append(bcc)

elif v != parent.get(u, None) and disc[v] < disc[u]:


stack.append((u, v)) # Store the back edge in stack
low[u] = min(low[u], disc[v])

# Initialize DFS traversal


for i in range(self.vertices):
if i not in visited:
dfs(i)

# Check for remaining edges in stack


if stack:
bcc = []
while stack:
bcc.append(stack.pop())
bccs.append(bcc)

return bccs

def main():
# Example graph
g = Graph(5)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(1, 3)
g.add_edge(2, 4)
g.add_edge(3, 4)

print("Biconnected Components:")
bccs = g.biconnected_components()
for i, bcc in enumerate(bccs):
print(f"Biconnected Component {i + 1}: {bcc}")

if __name__ == "__main__":
main()

OUTPUT

Biconnected Components:
Biconnected Component 1: [(3, 1), (4, 3), (2, 4), (2, 0), (1, 2), (0, 1)]

You might also like