Final - Lab Practical
Final - Lab Practical
ID NO: 62626
INDEX
Insertion in Binary Search Tree (BST):-A new key is always inserted at the leaf by
maintaining the property of the binary search tree. We start searching for a key from the root
until we hit a leaf node. Once a leaf node is found, the new node is added as a child of the
leaf node. The below steps are followed while we try to insert a node into a binary search
tree:
Check the value to be inserted (X) with the value of the current node (Y) we are at
If X is less than val move to the left subtree.
Otherwise, move to the right subtree.
Once the leaf node is reached, insert X to its right or left based on the relation
between X and the leaf node’s value.
ALGORITHM
Inorder Traversal
The inorder traversal operation in a Binary Search Tree visits all its nodes in the following
order −
Firstly, we traverse the left child of the root node/current node, if any.
Next, traverse the current node.
Lastly, traverse the right child of the current node, if any.
Algorithm:
1. START
2. Traverse the left subtree, recursively
3. Then, traverse the root node
4. Traverse the right subtree, recursively.
5. END
Preorder Traversal
The preorder traversal operation in a Binary Search Tree visits all its nodes. However, the
root node in it is first printed, followed by its left subtree and then its right subtree.
Algorithm:
1. START
2. Traverse the root node first.
3. Then traverse the left subtree, recursively
4. Later, traverse the right subtree, recursively.
5. END
Postorder Traversal
Like the other traversals, postorder traversal also visits all the nodes in a Binary Search Tree
and displays them. However, the left subtree is printed first, followed by the right subtree and
lastly, the root node.
Algorithm
1. START
2. Traverse the left subtree, recursively
3. Traverse the right subtree, recursively.
4. Then, traverse the root node
5. END
IMPLEMENTATION class Node:
def __init__(self, key):
self.left = None
self.right = None
self.key = key
def insert(root, key):
temp = Node(key)
if root is None:
return temp
parent = None
curr = root
while curr is not None:
parent = curr
if curr.key > key:
curr = curr.left
elif curr.key < key:
curr = curr.right
else:
return root
if parent.key > key:
parent.left = temp
else:
parent.right = temp
return root
def inorder(root):
if root:
inorder(root.left)
print(root.key, end=" ")
inorder(root.right)
def preorder(root):
if root:
print(root.key, end=" ")
preorder(root.left)
preorder(root.right)
def postorder(root):
if root:
postorder(root.left)
postorder(root.right)
print(root.key, end=" ")
r = Node(50)
r = insert(r, 10)
r = insert(r, 20)
r = insert(r, 30)
r = insert(r, 80)
r = insert(r, 60)
r = insert(r, 40)
print("Inorder traversal (Left, Root, Right):")
inorder(r)
print("\n\nPreorder traversal (Root, Left, Right):")
preorder(r)
print("\n\nPostorder traversal (Left, Right, Root):")
postorder(r)
OUTPUT
Step 2: END
IMPLENENTATION
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def search(root, key):
if root is None:
return False
if root.key == key:
return True
if root.key < key:
return search(root.right, key)
return search(root.left, key)
root = Node(50)
root.left = Node(30)
root.right = Node(70)
root.left.left = Node(20)
root.left.right = Node(40)
root.right.left = Node(60)
root.right.right = Node(80)
print(" Key 29 Found" if search(root, 29) else "Key 29 Not Found")
print("Key 70 Found" if search(root, 80) else " Key 70 not Found")
OUTPUT
Key 70 Found
LAB PRACTICAL-3
Objective: Program for Deletion in Binary Search Tree
Delete function is used to delete the specified node from a binary search tree. However, we
must delete a node from a binary search tree in such a way, that the property of binary search
tree doesn't violate. There are three situations of deleting a node from binary search tree.
1. The node to be deleted is a leaf node: It is the simplest case, in this case, replace the leaf
node with the NULL and simple free the allocated space.
2. The node to be deleted has only one child: In this case, replace the node with its child
and delete the child node, which now contains the value which is to be deleted. Simply
replace it with the NULL and free the allocated space.
3. The node to be deleted has two children: It is a bit complexed case compare to other two
cases. However, the node which is to be deleted, is replaced with its in-order successor or
predecessor recursively until the node value (to be deleted) is placed on the leaf of the tree.
After the procedure, replace the node with NULL and free the allocated space.
ALGORITHM
Delete (TREE, ITEM)
Step 2: END
IMPLEMENTATION
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def get_successor(curr):
curr = curr.right
while curr is not None and curr.left is not None:
curr = curr.left
return curr
def del_node(root, x):
if root is None:
return root
if root.key > x:
root.left = del_node(root.left, x)
elif root.key < x:
root.right = del_node(root.right, x)
else:
if root.left is None:
return root.right
if root.right is None:
return root.left
succ = get_successor(root)
root.key = succ.key
root.right = del_node(root.right, succ.key)
return root
def inorder(root):
if root is not None:
inorder(root.left)
print(root.key, end=" ")
inorder(root.right)
if __name__ == "__main__":
root = Node(10)
root.left = Node(5)
root.right = Node(15)
root.right.left = Node(12)
root.right.right = Node(18)
print("Initial BST (Inorder Traversal):")
inorder(root)
print()
x = 15
root = del_node(root, x)
print(f"BST after deleting {x} (Inorder Traversal):")
inorder(root)
print()
OUTPUT:
Update Heights:
After the insertion, update the height of the ancestor nodes.
Occurs when a node is inserted into the left subtree of the left child of the unbalanced node.
Perform a Right Rotation on the unbalanced node.
Occurs when a node is inserted into the right subtree of the right child of the unbalanced
node. Perform a Left Rotation on the unbalanced node.
Occurs when a node is inserted into the right subtree of the left child of the unbalanced
node. Perform a Left Rotation on the left child, followed by a Right Rotation on the
unbalanced node.
Steps:
1. Perform a Left Rotation on the left child of the unbalanced node to convert it into a
Left-Left case.
2. Perform a Right Rotation on the unbalanced node.
Occurs when a node is inserted into the left subtree of the right child of the unbalanced node.
Perform a Right Rotation on the right child, followed by a Left Rotation on the unbalanced node.
Steps:
1. Perform a Right Rotation on the right child of the unbalanced node to convert it into
a Right-Right case.
2. Perform a Left Rotation on the unbalanced node.
IMPLEMENTATION
class AVLNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
self.height = 1
def height(node):
if not node:
return 0
return node.height
# Function to calculate the balance factor of a node
def get_balance(node):
if not node:
return 0
return height(node.left) - height(node.right)
# Function for right rotation
def right_rotate(y):
x = y.left
T2 = x.right
x.right = y
y.left = T2
y.height = 1 + max(height(y.left), height(y.right))
x.height = 1 + max(height(x.left), height(x.right))
return x
# Function for left rotation
def left_rotate(x):
y = x.right
T2 = y.left
y.left = x
x.right = T2
x.height = 1 + max(height(x.left), height(x.right))
y.height = 1 + max(height(y.left), height(y.right))
return y
# Function to insert a node into the AVL Tree
def insert(node, key):
# Perform normal BST insertion
if not node:
return AVLNode(key)
if key < node.key:
node.left = insert(node.left, key)
elif key > node.key:
node.right = insert(node.right, key)
else: # Duplicate keys are not allowed
return node
# Update the height of the ancestor node
node.height = 1 + max(height(node.left), height(node.right))
# Get the balance factor
balance = get_balance(node)
# Perform rotations to balance the tree
# Case 1: Left-Left (LL)
if balance > 1 and key < node.left.key:
return right_rotate(node)
# Case 2: Right-Right (RR)
if balance < -1 and key > node.right.key:
return left_rotate(node)
# Case 3: Left-Right (LR)
if balance > 1 and key > node.left.key:
node.left = left_rotate(node.left)
return right_rotate(node)
# Case 4: Right-Left (RL)
if balance < -1 and key < node.right.key:
node.right = right_rotate(node.right)
return left_rotate(node)
return node
# Function to perform inorder traversal
def inorder(node):
if node:
inorder(node.left)
print(node.key, end=" ")
inorder(node.right)
# Main Program
if __name__ == "__main__":
# Create an empty AVL Tree
root = None
# Harcoded set of values for the AVL Tree
values = [20, 10, 30, 25, 40, 22, 50]
# Insert each value into the AVL Tree
for value in values:
root = insert(root, value)
# Display the AVL Tree using inorder traversal
print("Inorder traversal of the AVL Tree:")
inorder(root)
print()
OUTPUT
class AVLNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
self.height = 1
def height(node):
if not node:
return 0
return node.height
def get_balance(node):
if not node:
return 0
return height(node.left) - height(node.right)
def right_rotate(y):
x = y.left
T2 = x.right
x.right = y
y.left = T2
y.height = 1 + max(height(y.left), height(y.right))
x.height = 1 + max(height(x.left), height(x.right))
return x
def left_rotate(x):
y = x.right
T2 = y.left
y.left = x
x.right = T2
x.height = 1 + max(height(x.left), height(x.right))
y.height = 1 + max(height(y.left), height(y.right))
return y
def insert(node, key):
if not node:
return AVLNode(key)
if key < node.key:
node.left = insert(node.left, key)
elif key > node.key:
node.right = insert(node.right, key)
else: # Duplicate keys are not allowed
return node
node.height = 1 + max(height(node.left), height(node.right))
balance = get_balance(node)
if balance > 1 and key < node.left.key: # Left-Left (LL)
return right_rotate(node)
if balance < -1 and key > node.right.key: # Right-Right (RR)
return left_rotate(node)
if balance > 1 and key > node.left.key: # Left-Right (LR)
node.left = left_rotate(node.left)
return right_rotate(node)
if balance < -1 and key < node.right.key: # Right-Left (RL)
node.right = right_rotate(node.right)
return left_rotate(node)
return node
def search(node, key):
if not node:
return False
if node.key == key:
return True
elif key < node.key:
return search(node.left, key)
else:
return search(node.right, key)
def inorder(node):
if node:
inorder(node.left)
print(node.key, end=" ")
inorder(node.right)
if __name__ == "__main__":
# Create an empty AVL Tree
root = None
values = [20, 10, 30, 25, 40, 22, 50]
for value in values:
root = insert(root, value)
print("Inorder traversal of the AVL Tree:")
inorder(root)
print()
keys_to_search = [25, 15, 50, 60]
for key in keys_to_search:
result = "Found" if search(root, key) else "Not Found"
print(f"Search for {key}: {result}")
OUTPUT
class AVLNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
self.height = 1
def height(node):
if not node:
return 0
return node.height
def get_balance(node):
if not node:
return 0
return height(node.left) - height(node.right)
def right_rotate(y):
x = y.left
T2 = x.right
x.right = y
y.left = T2
y.height = 1 + max(height(y.left), height(y.right))
x.height = 1 + max(height(x.left), height(x.right))
return x
def left_rotate(x):
y = x.right
T2 = y.left
y.left = x
x.right = T2
x.height = 1 + max(height(x.left), height(x.right))
y.height = 1 + max(height(y.left), height(y.right))
return y
def get_min_value_node(node):
if node is None or node.left is None:
return node
return get_min_value_node(node.left)
def delete_node(root, key):
if not root:
return root
if key < root.key:
root.left = delete_node(root.left, key)
elif key > root.key:
root.right = delete_node(root.right, key)
else:
if not root.left:
return root.right
elif not root.right:
return root.left
temp = get_min_value_node(root.right)
root.key = temp.key
root.right = delete_node(root.right, temp.key)
root.height = 1 + max(height(root.left), height(root.right))
balance = get_balance(root)
# Left-Left (LL)
if balance > 1 and get_balance(root.left) >= 0:
return right_rotate(root)
# Left-Right (LR)
if balance > 1 and get_balance(root.left) < 0:
root.left = left_rotate(root.left)
return right_rotate(root)
# Right-Right (RR)
if balance < -1 and get_balance(root.right) <= 0:
return left_rotate(root)
# Right-Left (RL)
if balance < -1 and get_balance(root.right) > 0:
root.right = right_rotate(root.right)
return left_rotate(root)
return root
# Function to insert a node into the AVL Tree
def insert(root, key):
if not root:
return AVLNode(key)
if key < root.key:
root.left = insert(root.left, key)
elif key > root.key:
root.right = insert(root.right, key)
else:
return root
root.height = 1 + max(height(root.left), height(root.right))
balance = get_balance(root)
if balance > 1 and key < root.left.key:
return right_rotate(root)
if balance < -1 and key > root.right.key:
return left_rotate(root)
if balance > 1 and key > root.left.key:
root.left = left_rotate(root.left)
return right_rotate(root)
if balance < -1 and key < root.right.key:
root.right = right_rotate(root.right)
return left_rotate(root)
return root
def inorder(root):
if root:
inorder(root.left)
print(root.key, end=" ")
inorder(root.right)
if __name__ == "__main__":
root = None
# Insert nodes into the AVL Tree
values = [20, 10, 30, 5, 15, 25, 35]
for value in values:
root = insert(root, value)
print("Inorder traversal of the AVL Tree before deletion:")
inorder(root)
print()
# Delete a node from the AVL Tree
key_to_delete = 10
root = delete_node(root, key_to_delete)
print(f"Inorder traversal of the AVL Tree after deleting {key_to_delete}:")
inorder(root)
print()
OUTPUT
ALGORITHM
Insertion (L, Key)
local update [0...Max_Level + 1]
a = L → header
for i = L → level down to 0 do.
while a → forward[i] → key forward[i]
update[i] = a
a = a → forward[0]
lvl = random_Level()
if lvl > L → level then
for i = L → level + 1 to lvl do
update[i] = L → header
L → level = lvl
import random
# Node class for the Skip List
class Node:
def __init__(self, key, level):
self.key = key # The value of the node
self.forward = [None] * (level + 1) # Pointers to next nodes at different levels
# Skip List class
class SkipList:
def __init__(self, max_level, p):
self.max_level = max_level # Maximum level of the Skip List
self.p = p # Probability for random level generation
self.header = self.create_node(None, max_level) # Create a header node
self.level = 0 # Current level of the Skip List
# Create a node with a given key and level
def create_node(self, key, level):
return Node(key, level)
# Generate a random level for a new node
def random_level(self):
level = 0
while random.random() < self.p and level < self.max_level:
level += 1
return level
# Insert a key into the Skip List
def insert(self, key):
# Create an update array to keep track of nodes at each level
update = [None] * (self.max_level + 1)
current = self.header
# Traverse the Skip List from top to bottom
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
update[i] = current
print(f"Inserted
# Move to the nextkey
node {key} at level {new_level}")
# Print
current the=Skip List for visualization
current.forward[0]
def# display(self):
If the key does not already exist
print("\nSkip
if current is None List:")
or current.key != key:
for#iGenerate
in range(self.level
a random +level1): for the new node
current = self.header.forward[i]
new_level = self.random_level()
#print(f"Level
If the random {i}: ", end="")
level is greater than the current level of the list
while current:
if new_level > self.level:
print(current.key,
for end="+")1, new_level + 1):
i in range(self.level
current = current.forward[i]
update[i] = self.header
print()
self.level = new_level
# Main #Function
Create a new node with the key and random level
if __name__
new_node== "__main__":
= self.create_node(key, new_level)
max_level = 4 # Maximum
# Adjust pointers for eachlevels in the Skip List
level
p = 0.5for i in range(new_level + 1): generation
# Probability factor for level
skip_listnew_node.forward[i]
= SkipList(max_level, p)
= update[i].forward[i]
# Insertupdate[i].forward[i]
keys into the Skip List = new_node
keys = [3, 6, 7, 9, 12, 19, 17, 26, 21]
for key in keys:
skip_list.insert(key)
# Displaying the Skip List
skip_list.display()
OUTPUT
Skip List:
Level 0: 3 6 7 9 12 17 19 21 26
Level 1: 3 6 9 17 19 21 26
Level 2: 3 6 9
LAB PRACTICAL-8
Objective: Program for Searching in Skip List.
Searching an element is very similar to approach for searching a spot for inserting an element
in Skip list. The basic idea is if –
1. Key of next node is less than search key then we keep on moving forward on the same
level.
2. Key of next node is greater than the key to be inserted then we store the pointer to
current node i at update[i] and move one level down and continue our search.
At the lowest level (0), if the element next to the rightmost element (update[0]) has key equal
to the search key, then we have found key otherwise failure.
ALGORITHM
Search(list, searchKey)
x := list -> header
-- loop invariant: x -> key level downto 0 do
while x -> forward[i] -> key forward[i]
x := x -> forward[0]
if x -> key = searchKey then return x -> value
else return failure
IMPLEMENTATION
import random
# Node class for the Skip List
class Node:
def __init__(self, key, level):
self.key = key # The value of the node
self.forward = [None] * (level + 1) # Pointers to next nodes at different levels
# Skip List class
class SkipList:
def __init__(self, max_level, p):
self.max_level = max_level # Maximum level of the Skip List
def insert(self, key):
update = [None] * (self.max_level + 1)
current = self.header
# Traverse the Skip List to find the position to insert
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
update[i] = current
current = current.forward[0]
if current is None or current.key != key:
new_level = self.random_level()
if new_level > self.level:
for i in range(self.level + 1, new_level + 1):
update[i] = self.header
self.level = new_level
new_node = self.create_node(key, new_level)
for i in range(new_level + 1):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
print(f"Inserted key {key} at level {new_level}")
# Search for a key in the Skip List
def search(self, key):
current = self.header
# Traverse the Skip List from top to bottom
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
# Move to the next node
current = current.forward[0]
# If the current node's key matches the search key
if current and current.key == key:
print(f"Key {key} found in the Skip List")
return True
else:
print(f"Key {key} not found in the Skip List")
return False
# Print the Skip List
def display(self):
print("\nSkip List:")
for i in range(self.level + 1):
current = self.header.forward[i]
print(f"Level {i}: ", end="")
while current:
print(current.key, end=" ")
current = current.forward[i]
print()
if __name__ == "__main__":
max_level = 4 # Maximum levels in the Skip List
p = 0.5 # Probability factor for level generation
skip_list = SkipList(max_level, p)
# Insert keys into the Skip List
keys = [3, 6, 7, 9, 12, 19, 17, 26, 21]
for key in keys:
skip_list.insert(key)
# Display the Skip List
skip_list.display()
# Search for keys in the Skip List
search_keys = [3, 19, 50]
for key in search_keys:
skip_list.search(key)
OUTPUT
PS C:\Users\joshi\Desktop\mtech\ADS Codes> python -u "c:\Users\joshi\Desktop\mtech\ADS
Codes\skiplistsearch.py"
Inserted key 3 at level 0
Inserted key 6 at level 2
Inserted key 7 at level 1
Inserted key 9 at level 1
Inserted key 12 at level 1
Inserted key 19 at level 0
Inserted key 17 at level 0
Inserted key 26 at level 0
Inserted key 21 at level 1
Skip List:
Level 0: 3 6 7 9 12 17 19 21 26
Level 1: 6 7 9 12 21
Level 2: 6
ALGORITHM
Deletion (L, Key)
local update [0... Max_Level + 1]
a = L → header
for i = L → level down to 0 do.
while a → forward[i] → key forward[i]
update[i] = a
a = a → forward[0]
if a → key = Key then
for i = 0 to L → level do
if update[i] → forward[i] ? a then break
update[i] → forward[i] = a → forward[i]
free(a)
while L → level > 0 and L → header → forward[L → level] = NIL do
L → level = L → level - 1
IMPLEMENTATION:
import random
# Node class for the Skip List
class Node:
def __init__(self, key, level):
self.key = key # The value of the node
self.forward = [None] * (level + 1) # Pointers to next nodes at different levels
# Skip List class
class SkipList:
def __init__(self, max_level, p):
self.max_level = max_level # Maximum level of the Skip List
self.p = p # Probability factor for level generation
self.header = self.create_node(None, max_level) # Create a header node
self.level = 0 # Current level of the Skip List
# Create a node with a given key and level
def create_node(self, key, level):
return Node(key, level)
# Generate a random level for a new node
def random_level(self):
level = 0
while random.random() < self.p and level < self.max_level:
level += 1
return level
# Insert a key into the Skip List
def insert(self, key):
current = self.header
# Traverse the Skip List to find the position to insert
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
update[i] = current
current = current.forward[0]
if current is None or current.key != key:
new_level = self.random_level()
if new_level > self.level:
for i in range(self.level + 1, new_level + 1):
update[i] = self.header
self.level = new_level
new_node = self.create_node(key, new_level)
for i in range(new_level + 1):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
print(f"Inserted key {key} at level {new_level}")
# Search for a key in the Skip List
def search(self, key):
current = self.header
# Traverse the Skip List from top to bottom
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
current = current.forward[0]
# If the current node's key matches the search key
if current and current.key == key:
print(f"Key {key} found in the Skip List")
return True
else:
print(f"Key {key} not found in the Skip List")
return False
# Delete a key from the Skip List
def delete(self, key):
update = [None] * (self.max_level + 1)
current = self.header
# Traverse the Skip List to find the key
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
update[i] = current
current = current.forward[0]
# If the key is found
if current and current.key == key:
for i in range(self.level + 1):
if update[i].forward[i] != current:
break
update[i].forward[i] = current.forward[i]
# Remove levels that are now empty
while self.level > 0 and self.header.forward[self.level] is None:
self.level -= 1
print(f"Key {key} deleted from the Skip List")
else:
print(f"Key {key} not found in the Skip List")
def display(self):
print("\nSkip List:")
for i in range(self.level + 1):
current = self.header.forward[i]
print(f"Level {i}: ", end="")
while current:
print(current.key, end=" ")
current = current.forward[i]
print()
if __name__ == "__main__":
max_level = 4 # Maximum levels in the Skip List
p = 0.5 # Probability factor for level generation
skip_list = SkipList(max_level, p)
keys = [3, 6, 7, 9, 12, 19, 17, 26, 21]
for key in keys:
skip_list.insert(key)
skip_list.display()
delete_keys = [3, 19, 50]
for key in delete_keys:
skip_list.delete(key)
skip_list.display()
OUTPUT:
The naive algorithm finds all valid shifts using a loop that checks the condition P [1.......m] =
T [s+1.......s+m] for each of the n - m +1 possible value of s.
ALGORITHM
NAIVE-STRING-MATCHER (T, P)
1. n ← length [T]
2. m ← length [P]
3. for s ← 0 to n -m
4. do if P [1.....m] = T [s + 1....s + m]
5. then print "Pattern occurs with shift" s
IMPLEMENTATION
OUTPUT
IMPLEMENTATION
def rabin_karp_search(text, pattern, q):
M = len(pattern)
N = len(text)
d = 256
p=0
t=0
h=1
# h = (d^(M-1)) % q
for i in range(M-1):
h = (h * d) % q
for i in range(M):
p = (d * p + ord(pattern[i])) % q
t = (d * t + ord(text[i])) % q
# Slide the pattern over the text one by one
for i in range(N - M + 1):
if p == t:
for j in range(M):
if text[i + j] != pattern[j]:
break
if j == M - 1:
print(f"Pattern found at index {i}")
if i < N - M:
t = (d * (t - ord(text[i]) * h) + ord(text[i + M])) % q
if t < 0:
t=t+q
if __name__ == "__main__":
text = "AAAAKANKSHAJOSHIJKJK"
pattern = "JOSHIJKJK"
q = 101
rabin_karp_search(text, pattern, q)
OUTPUT
LAB PRACTICAL-12
Objective: Program for Hashing using Open Addressing.
THEORY:
Hashing is a technique used to store and retrieve data efficiently. In open addressing, when
a collision occurs (i.e., two keys hash to the same index), the ALGORITHM searches for the
next available slot in the hash table using a probing sequence. There are several probing
techniques, including:
1. Linear Probing: In the case of a collision, check the next slot sequentially (i.e., index
+ 1).
2. Quadratic Probing: In the case of a collision, check the next slot with a quadratic
step (i.e., index + 1², index + 2², etc.).
3. Double Hashing: Use a second hash function to determine the step size when a
collision occurs.
For this practical, we will implement Linear Probing as a method of open addressing.
ALGORITHM for Hashing using Linear Probing
1. Initialize the hash table with a fixed size, all set to None (empty).
2. Insertion:
o Calculate the hash value of the key.
o If the slot is occupied, use linear probing to find the next available slot.
3. Search:
o Calculate the hash value of the key.
o If the slot is empty or contains the desired key, return the result. If not,
continue checking subsequent slots using linear probing.
4. Deletion:
o Find the key using linear probing.
o Once found, remove the key and shift the subsequent keys, if necessary, to
maintain the probing sequence.
IMPLEMENTATION
class HashTable:
def _init_(self, size):
self.size = size
self.table = [None] * self.size
def hash_function(self, key):
return key % self.size
def linear_probe(self, key):
index = self.hash_function(key)
original_index = index
while self.table[index] is not None and self.table[index] != "DELETED":
if self.table[index] == key:
return index # If the key is found, return the index
index = (index + 1) % self.size
return None (key not found)
if index == original_index:
return None
return index # If an empty slot is found, return that index
def insert(self, key):
index = self.linear_probe(key)
if index is not None:
self.table[index] = key
else:
print("Hash Table is full! Cannot insert key.")
def delete(self, key):
index = self.linear_probe(key)
if index is not None:
self.table[index] = "DELETED"
else:
print("Key not found in the hash table.")
def search(self, key):
index = self.linear_probe(key)
if index is None:
return None # Key not found
return self.table[index]
def display(self):
for index in range(self.size):
if self.table[index] is None:
print(f"Index {index}: None")
elif self.table[index] == "DELETED":
print(f"Index {index}: DELETED")
else:
print(f"Index {index}: {self.table[index]}")
if _name_ == "_main_":
hash_table = HashTable(7) # Create a hash table with size 7
# Insert some keys
hash_table.insert(10)
hash_table.insert(20)
hash_table.insert(15)
hash_table.insert(7)
print("Hash Table after insertions:")
hash_table.display()
print("\nSearch for key 15:", hash_table.search(15)) # Should return 15
print("Search for key 30:", hash_table.search(30)) # Should return None
hash_table.delete(20)
print("\nHash Table after deletion of key 20:")
hash_table.display()
print("\nSearch for deleted key 20:", hash_table.search(20)) # Should return None
OUTPUT