EXPERIMENT NO1:- Implementation of data structure operations (insertion,
deletion, traversing and searching) in array. Linear search and binary search
Solve:-
Inserting an Element to an array using array module
Program code:
import array
arr1 = array.array('i', [1, 2, 3])
arr2 = array.array('i', [4, 5, 6]) print("arr1 is:", arr1) print("arr2 is:", arr2)
arr3 = arr1 + arr2
print("After arr3 = arr1 + arr2, arr3 is:", arr3)
Output:
arr1 is: array('i', [1, 2, 3])
arr2 is: array('i', [4, 5, 6])
After arr3 = arr1 + arr2, arr3 is: array('i', [1, 2, 3, 4, 5, 6])
import array
arr1 = array.array('i', [1, 2, 3])
arr2 = array.array('i', [4, 5, 6]) print("arr1 is:", arr1) print("arr2 is:", arr2)
arr1.append(4)
print("\nAfter arr1.append(4), arr1 is:", arr1) arr1.extend(arr2)
print("\nAfter arr1.extend(arr2), arr1 is:", arr1) arr1.insert(0, 10)
print("\nAfter arr1.insert(0, 10), arr1 is:", arr1)
Output:
arr1 is: array('i', [1, 2, 3])
arr2 is: array('i', [4, 5, 6])
After arr1.append(4), arr1 is: array('i', [1, 2, 3, 4])
After arr1.extend(arr2), arr1 is: array('i', [1, 2, 3, 4, 4, 5, 6])
After arr1.insert(0, 10), arr1 is: array('i', [10, 1, 2, 3, 4, 4, 5, 6])
Deletion an Element to an array using array module:
Program Code:
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
lst.remove(2) print(lst) lst.pop(0) print(lst)
lst.pop() print(lst) del lst[0] print(lst) del lst
lst = [1, 2, 3, 4]
print(lst)
lst.clear() print(lst)
output
[1, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 4, 5, 6, 7, 8, 9, 10]
[3, 4, 5, 6, 7, 8, 9]
[4, 5, 6, 7, 8, 9]
[1, 2, 3, 4]
EXPERIMENT NO2:- Implement of Stack, queue operation using array. Pop,
Push, Insertion, deletion, Implementation of Circular queue. Infix to postfix
expression evaluation. Implementation of stack operations in data structure:
Implementation of stack operations in data structure:
Programme code :
class Stack:
def __init__(self):
self.stack = []
def is_empty(self):
return len(self.stack) == 0
def push(self, item):
self.stack.append(item)
def pop(self):
if not self.is_empty():
return self.stack.pop()
else:
print("Stack is empty. Cannot pop.")
return None
def peek(self):
if not self.is_empty():
return self.stack[-1]
else:
print("Stack is empty. Cannot peek.")
return None
def size(self):
return len(self.stack)
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print("Stack:", stack.stack)
print("Peek:", stack.peek())
print("Pop:", stack.pop())
print("Stack:", stack.stack)
print("Is Empty?", stack.is_empty())
print("Stack Size:", stack.size())
output
Stack: [1, 2, 3]
Peek: 3
Pop: 3
Stack: [1, 2]
Is Empty? False
Stack Size: 2
Implementation of circular queue operations in data structure:
class CircularQueue:
def __init__(self, size):
self.maxSize = size
self.queueArray = [0] * size
self.front = -1
self.rear = -1
def enqueue(self, item):
if self.isEmpty():
self.front = 0
self.rear = 0
self.queueArray[self.rear] = item
else:
self.rear = (self.rear + 1) % self.maxSize
if self.rear == self.front:
print("Queue is full. Cannot enqueue.")
self.rear = (self.rear - 1 + self.maxSize) % self.maxSize
else:
self.queueArray[self.rear] = item
def dequeue(self):
item = -1 # Assuming -1 represents an empty value
if not self.isEmpty():
item = self.queueArray[self.front]
if self.front == self.rear:
self.front = -1
self.rear = -1
else:
self.front = (self.front + 1) % self.maxSize
else:
print("Queue is empty. Cannot dequeue.")
return item
def peek(self):
if not self.isEmpty():
return self.queueArray[self.front]
else:
print("Queue is empty. No peek value.")
return -1 # Assuming -1 represents an empty value
def isEmpty(self):
return self.front == -1 and self.rear == -1
if __name__ == "__main__":
circularQueue = CircularQueue(5)
circularQueue.enqueue(1)
circularQueue.enqueue(2)
circularQueue.enqueue(3)
# Should print 1
print("Peek:", circularQueue.peek())
# Should print 1
print("Dequeue:", circularQueue.dequeue())
# Should print 2
print("Peek after dequeue:", circularQueue.peek())
output
Peek: 1
Dequeue: 1
Peek after dequeue: 2
EXPERIMENT NO3:- Implementation of linked lists: Single linked list,
circular linked list, double linked list, doubly circular linked list. Implementation
of stack and queue using linked list. Merging two linked list, Linked list
representation of a polynomial, polynomial addition, polynomial multiplication.
Implementation of single linked list in data structure:
Insertion of linked list
class Node:
def __init__(self, data):
self.data = data
self.next = None
def insert_at_beginning(head, data):
new_node = Node(data)
new_node.next = head
return new_node
def insert_at_end(head, data):
new_node = Node(data)
if head is None:
return new_node
current = head
while current.next:
current = current.next
current.next = new_node
return head
def traverse(head):
current = head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
head = None
head = insert_at_beginning(head, 3)
head = insert_at_beginning(head, 2)
head = insert_at_beginning(head, 1)
insert_at_end(head, 4)
traverse(head)
output
1 -> 2 -> 3 -> 4 -> None
Deletion of the Single Linked list
class Node:
def __init__(self, data):
self.data = data
self.next = None
def insert_at_beginning(head, data):
new_node = Node(data)
new_node.next = head
return new_node
def delete_at_beginning(head):
if head is None:
print("Error: Singly linked list is empty")
return None
new_head = head.next
del head
return new_head
def traverse(head):
current = head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
head = None
head = insert_at_beginning(head, 4)
head = insert_at_beginning(head, 3)
head = insert_at_beginning(head, 2)
head = insert_at_beginning(head, 1)
head = delete_at_beginning(head)
traverse(head)
output
2 -> 3 -> 4 -> None
Implementation of the circular linked list in Data Structure:
Insertion in Circular Linked List:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def insert_at_beginning(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = self.head
else:
new_node.next = self.head
temp = self.head
while temp.next != self.head:
temp = temp.next
temp.next = new_node
self.head = new_node
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
linked_list = LinkedList()
linked_list.insert_at_beginning(3)
linked_list.insert_at_beginning(2)
linked_list.insert_at_beginning(1)
print("Linked List after insertion at the beginning:")
linked_list.display()
output
Linked List after insertion at the beginning:
1 -> 2 -> 3 -> ... (circular)
Deletion in Circular Linked List:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
new_node.next = new_node
self.head = new_node
else:
current = self.head
while current.next != self.head:
current = current.next
current.next = new_node
new_node.next = self.head
def delete_at_beginning(self):
if not self.head:
print("Circular Linked List is empty")
return
if self.head.next == self.head:
self.head = None
return
current = self.head
while current.next != self.head:
current = current.next
current.next = self.head.next
self.head = self.head.next
def display(self):
if not self.head:
print("Circular Linked List is empty")
return
current = self.head
while True:
print(current.data, end=" -> ")
current = current.next
if current == self.head:
break
print("", end="")
circular_list = CircularLinkedList()
circular_list.append(1)
circular_list.append(2)
circular_list.append(3)
print("Circular Linked List before deletion:")
circular_list.display()
print()
circular_list.delete_at_beginning()
print("Circular Linked List after deletion at the beginning:")
circular_list.display()
output
Circular Linked List before deletion:
1 -> 2 -> 3 ->
Circular Linked List after deletion at the beginning:
2 -> 3 ->
Implementation of double linked list in data structure:
Insertion of double linked list:
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def insert_at_beginning(head, data):
new_node = Node(data)
new_node.next = head
if head:
head.prev = new_node
return new_node
def display(head):
current = head
while current:
print(current.data, end=" <-> ")
current = current.next
print("None")
head = None
head = insert_at_beginning(head, 3)
head = insert_at_beginning(head, 2)
head = insert_at_beginning(head, 1)
print("Doubly Linked List after insertion at the beginning:")
display(head)
output
Doubly Linked List after insertion at the beginning:
1 <-> 2 <-> 3 <-> None
Deletion of double linked list:
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def delete_at_beginning(head):
if head is None:
print("Doubly linked list is empty")
return None
if head.next is None:
return None
new_head = head.next
new_head.prev = None
del head
return new_head
def traverse(head):
current = head
while current:
# Print current node's data
print(current.data, end=" <-> ")
# Move to the next node
current = current.next
print("None")
def insert_at_beginning(head, data):
new_node = Node(data)
new_node.next = head
if head:
head.prev = new_node
return new_node
head = None
head = insert_at_beginning(head, 4)
head = insert_at_beginning(head, 3)
head = insert_at_beginning(head, 2)
head = insert_at_beginning(head, 1)
head = delete_at_beginning(head)
traverse(head)
output
1 <-> 2 <-> 3 <-> 4 <-> None
Implementation of doubly Circular linked list in data structure:
Insertion of the Doubly circular linked list:
class Node:
def __init__(self, x):
self.data = x
self.next = None
self.prev = None
def insertAtBeginning(head, newData):
newNode = Node(newData)
if head is None:
newNode.next = newNode.prev = newNode
head = newNode
else:
last = head.prev
newNode.next = head
newNode.prev = last
head.prev = newNode
last.next = newNode
head = newNode
return head
def printList(head):
if not head:
return
curr = head
while True:
print(curr.data, end=" ")
curr = curr.next
if curr == head:
break
print()
head = Node(10)
head.next = Node(20)
head.next.prev = head
head.next.next = Node(30)
head.next.next.prev = head.next
head.next.next.next = head
head.prev = head.next.next
head = insertAtBeginning(head, 5)
printList(head)
output
5 10 20 30
Implementation of queue using linked list:
class Node:
def __init__(self, new_data):
self.data = new_data
self.next = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def is_empty(self):
return self.front is None and self.rear is None
def enqueue(self, new_data):
new_node = Node(new_data)
if self.rear is None:
self.front = self.rear = new_node
return
self.rear.next = new_node
self.rear = new_node
def dequeue(self):
if self.is_empty():
print("Queue Underflow")
return
temp = self.front
self.front = self.front.next
if self.front is None:
self.rear = None
def get_front(self):
if self.is_empty():
print("Queue is empty")
return float('-inf')
return self.front.data
def get_rear(self):
if self.is_empty():
print("Queue is empty")
return float('-inf')
return self.rear.data
if __name__ == "__main__":
q = Queue()
q.enqueue(10)
q.enqueue(20)
print("Queue Front:", q.get_front())
print("Queue Rear:", q.get_rear())
q.dequeue()
q.dequeue()
q.enqueue(30)
q.enqueue(40)
q.enqueue(50)
q.dequeue()
print("Queue Front:", q.get_front())
print("Queue Rear:", q.get_rear())
output
Queue Front: 10
Queue Rear: 20
Queue Front: 40
Queue Rear: 50
EXPERIMENT 4: Tree: creating Binary Search tree, recursive and non
recursive traversal of BST, deletion in BST, calculating height of a BST, building
AVL tree.
CREATING BINARY SEARCH TREE:
Class BSTNode:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def insert(self, val):
if not self.val:
self.val = val
return
if self.val == val:
return
if val < self.val:
if self.left:
self.left.insert(val)
return
self.left = BSTNode(val)
return
if self.right:
self.right.insert(val)
return
self.right = BSTNode(val)
def get_min(self):
current = self
while current.left is not None:
current = current.left
return current.val
def get_max(self):
current = self
while current.right is not None:
current = current.right
return current.val
def delete(self, val):
if self == None:
return self
if self.right == None:
return self.left
if self.left == None:
return self.right
if val < self.val:
if self.left:
self.left = self.left.delete(val)
return self
if val > self.val:
if self.right:
self.right = self.right.delete(val)
return self
min_larger_node = self.right
while min_larger_node.left:
min_larger_node = min_larger_node.left
self.val = min_larger_node.val
self.right = self.right.delete(min_larger_node.val)
return self
def exists(self, val):
if val == self.val:
return True
if val < self.val:
if self.left == None:
return False
return self.left.exists(val)
if self.right == None:
return False
return self.right.exists(val)
def preorder(self, vals):
if self.val is not None:
vals.append(self.val)
if self.left is not None:
self.left.preorder(vals)
if self.right is not None:
self.right.preorder(vals)
return vals
def inorder(self, vals):
if self.left is not None:
self.left.inorder(vals)
if self.val is not None:
vals.append(self.val)
if self.right is not None:
self.right.inorder(vals)
return vals
def postorder(self, vals):
if self.left is not None:
self.left.postorder(vals)
if self.right is not None:
self.right.postorder(vals)
if self.val is not None:
vals.append(self.val)
return vals
output
Inorder Traversal: [20, 30, 40, 50, 60, 70, 80]
Preorder Traversal: [50, 30, 20, 40, 70, 60, 80]
Postorder Traversal: [20, 40, 30, 60, 80, 70, 50]
Minimum value: 20
Maximum value: 80
Does 40 exist? True
Does 100 exist? False
Inorder after deleting 20: [30, 40, 50, 60, 70, 80]
DELETION IN BST:
class TreeNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def inorder_traversal(root):
if root:
inorder_traversal(root.left)
print(root.key, end=" ")
inorder_traversal(root.right)
def find_min(node):
current = node
while current.left:
current = current.left
return current
def delete_node(root, key):
if root is None:
return root
# Search for the node to be deleted
if key < root.key:
root.left = delete_node(root.left, key)
elif key > root.key:
root.right = delete_node(root.right, key)
else:
# Node with only one child or no child
if root.left is None:
return root.right
elif root.right is None:
return root.left
# Node with two children: Get the in-order successor
temp = find_min(root.right)
# Copy the in-order successor's content to this node
root.key = temp.key
# Delete the in-order successor
root.right = delete_node(root.right, temp.key)
return root
if __name__ == "__main__":
root = TreeNode(50)
root.left = TreeNode(30)
root.right = TreeNode(70)
root.left.left = TreeNode(20)
root.left.right = TreeNode(40)
root.right.left = TreeNode(60)
root.right.right = TreeNode(80)
print("Original BST:")
inorder_traversal(root)
print("\n")
key_to_delete = 40
root = delete_node(root, key_to_delete)
print(f"BST after deleting {key_to_delete}:")
inorder_traversal(root)
output
Original BST:
20 30 40 50 60 70 80
BST after deleting 40:
20 30 50 60 70 80
CALCULATING HEIGHT OF BST:
# define a Class Tree, to intiate the binary tree
class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def height(root):
# Check if the binary tree is empty
if root is None:
# If TRUE return 0
return 0
# Recursively call height of each node
leftAns = height(root.left)
rightAns = height(root.right)
# Return max(leftHeight, rightHeight) at each iteration
return max(leftAns, rightAns) + 1
# Test the algorithm
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
print("Height of the binary tree is: " + str(height(root)))
output
Height of the binary tree is: 3
BUILDING AVL TREE:
import sys
# Create a tree node
class TreeNode(object):
def __init__(self, key):
self.key = key
self.left = None
self.right = None
self.height = 1
class AVLTree(object):
# Function to insert a node
def insert_node(self, root, key):
# Find the correct location and insert the node
if not root:
return TreeNode(key)
elif key < root.key:
root.left = self.insert_node(root.left, key)
else:
root.right = self.insert_node(root.right, key)
root.height = 1 + max(self.getHeight(root.left),
self.getHeight(root.right))
# Update the balance factor and balance the tree
balanceFactor = self.getBalance(root)
if balanceFactor > 1:
if key < root.left.key:
return self.rightRotate(root)
else:
root.left = self.leftRotate(root.left)
return self.rightRotate(root)
if balanceFactor < -1:
if key > root.right.key:
return self.leftRotate(root)
else:
root.right = self.rightRotate(root.right)
return self.leftRotate(root)
return root
# Function to delete a node
def delete_node(self, root, key):
# Find the node to be deleted and remove it
if not root:
return root
elif key < root.key:
root.left = self.delete_node(root.left, key)
elif key > root.key:
root.right = self.delete_node(root.right, key)
else:
if root.left is None:
temp = root.right
root = None
return temp
elif root.right is None:
temp = root.left
root = None
return temp
temp = self.getMinValueNode(root.right)
root.key = temp.key
root.right = self.delete_node(root.right,
temp.key)
if root is None:
return root
# Update the balance factor of nodes
root.height = 1 + max(self.getHeight(root.left),
self.getHeight(root.right))
balanceFactor = self.getBalance(root)
# Balance the tree
if balanceFactor > 1:
if self.getBalance(root.left) >= 0:
return self.rightRotate(root)
else:
root.left = self.leftRotate(root.left)
return self.rightRotate(root)
if balanceFactor < -1:
if self.getBalance(root.right) <= 0:
return self.leftRotate(root)
else:
root.right = self.rightRotate(root.right)
return self.leftRotate(root)
return root
# Function to perform left rotation
def leftRotate(self, z):
y = z.right
T2 = y.left
y.left = z
z.right = T2
z.height = 1 + max(self.getHeight(z.left),
self.getHeight(z.right))
y.height = 1 + max(self.getHeight(y.left),
self.getHeight(y.right))
return y
# Function to perform right rotation
def rightRotate(self, z):
y = z.left
T3 = y.right
y.right = z
z.left = T3
z.height = 1 + max(self.getHeight(z.left),
self.getHeight(z.right))
y.height = 1 + max(self.getHeight(y.left),
self.getHeight(y.right))
return y
# Get the height of the node
def getHeight(self, root):
if not root:
return 0
return root.height
# Get balance factore of the node
def getBalance(self, root):
if not root:
return 0
return self.getHeight(root.left) - self.getHeight(root.right)
def getMinValueNode(self, root):
if root is None or root.left is None:
return root
return self.getMinValueNode(root.left)
def preOrder(self, root):
if not root:
return
print("{0} ".format(root.key), end="")
self.preOrder(root.left)
self.preOrder(root.right)
# Print the tree
def printHelper(self, currPtr, indent, last):
if currPtr != None:
sys.stdout.write(indent)
if last:
sys.stdout.write("R----")
indent += " "
else:
sys.stdout.write("L----")
indent += "| "
print(currPtr.key)
self.printHelper(currPtr.left, indent, False)
self.printHelper(currPtr.right, indent, True)
myTree = AVLTree()
root = None
nums = [33, 13, 52, 9, 21, 61, 8, 11]
for num in nums:
root = myTree.insert_node(root, num)
myTree.printHelper(root, "", True)
key = 13
root = myTree.delete_node(root, key)
print("After Deletion: ")
myTree.printHelper(root, "", True)
output
R----33
L----13
| L----9
| | L----8
| | R----11
| R----21
R----52
R----61
After Deletion:
R----33
L----9
| L----8
| R----21
| L----11
R----52
R----61
EXPERIMENT 5: Implementation of sorting techniques: selection, bubble,
quick sort, insertion sort, merge sort, heap sot, implementation of priority queue.
Hash table implementation.
SELECTION SORT:
# Selection sort in Python
def selectionSort(array, size):
for step in range(size):
min_idx = step
for i in range(step + 1, size):
# to sort in descending order, change > to < in this line
# select the minimum element in each loop
if array[i] < array[min_idx]:
min_idx = i
# put min at the correct position
(array[step], array[min_idx]) = (array[min_idx], array[step])
data = [-2, 45, 0, 11, -9]
size = len(data)
selectionSort(data, size)
print('Sorted Array in Ascending Order:')
print(data)
output
Sorted Array in Ascending Order:
[-9, -2, 0, 11, 45]
BUBBLE SORT:
# Bubble sort in Python
def bubbleSort(array):
# loop to access each array element
for i in range(len(array)):
# loop to compare array elements
for j in range(0, len(array) - i - 1):
# compare two adjacent elements
# change > to < to sort in descending order
if array[j] > array[j + 1]:
# swapping elements if elements
# are not in the intended order
temp = array[j]
array[j] = array[j+1]
array[j+1] = temp
data = [-2, 45, 0, 11, -9]
bubbleSort(data)
print('Sorted Array in Ascending Order:')
print(data)
output
Sorted Array in Ascending Order:
[-9, -2, 0, 11, 45]
QUICK SORT:
# Quick sort in Python
# function to find the partition position
def partition(array, low, high):
# choose the rightmost element as pivot
pivot = array[high]
# pointer for greater element
i = low - 1
# traverse through all elements
# compare each element with pivot
for j in range(low, high):
if array[j] <= pivot:
# if element smaller than pivot is found
# swap it with the greater element pointed by i
i=i+1
# swapping element at i with element at j
(array[i], array[j]) = (array[j], array[i])
# swap the pivot element with the greater element specified by i
(array[i + 1], array[high]) = (array[high], array[i + 1])
# return the position from where partition is done
return i + 1
# function to perform quicksort
def quickSort(array, low, high):
if low < high:
# find pivot element such that
# element smaller than pivot are on the left
# element greater than pivot are on the right
pi = partition(array, low, high)
# recursive call on the left of pivot
quickSort(array, low, pi - 1)
# recursive call on the right of pivot
quickSort(array, pi + 1, high)
data = [8, 7, 2, 1, 0, 9, 6]
print("Unsorted Array")
print(data)
size = len(data)
quickSort(data, 0, size - 1)
print('Sorted Array in Ascending Order:')
print(data)
output
Unsorted Array
[8, 7, 2, 1, 0, 9, 6]
Sorted Array in Ascending Order:
[0, 1, 2, 6, 7, 8, 9]
INSERTION SORT:
# Insertion sort in Python
def insertionSort(array):
for step in range(1, len(array)):
key = array[step]
j = step - 1
# Compare key with each element on the left of it until an element smaller
than it is found
# For descending order, change key<array[j] to key>array[j].
while j >= 0 and key < array[j]:
array[j + 1] = array[j]
j=j-1
# Place key at after the element just smaller than it.
array[j + 1] = key
data = [9, 5, 1, 4, 3]
insertionSort(data)
print('Sorted Array in Ascending Order:')
print(data)
output
Sorted Array in Ascending Order:
[1, 3, 4, 5, 9]