Practical Paper-II: Data Structures Using Python (Lab)
[2 HPW :: 1 Credit :: 25 Marks]
1.Write programs to implement the following using an array: a) Stack ADT b)
Queue ADT.
Program:
Class stack:
def__init__(self):
self.values=[]
def push(self,x):
self.values = [x]+self.values
def pop(self):
retrun self.values.pop(0)
s=stack()
s.push(10)
s.push(20)
s.push(30)
s.push(40)
s.push(50)
print(s.values)
print(s.pop())
print(s.values)
OUTPUT:
[50,40,30,20,10]
50
[40,30,20,10]
b) program:
class queue:
def__init__(self):
self.values=[]
def enqueue(self,x):
self.values.append(x)
def dequeue(self):
front=self.values[0]
self.values=self.values[1:]
return front
q1=queue()
q1.enqueue(10)
q1.enqueue(20)
q1.enqueue(30)
q1.enqueue(40)
print(q1.values)
q1.enqueue(50)
print(q1.values)
print(q1.dequeue())
print(q1.values)
OUTPUT:
[10,20,30,40]
[10,20,30,40,50]
10
[20,30,40,50]
2.Write a program to convert the given infix expression to postfix expression
using stack.
Program:
# Function to determine precedence of operators
def precedence(op):
if op == '^':
return 3
elif op in ('*', '/'):
return 2
elif op in ('+', '-'):
return 1
return 0
# Function to check if the character is an operand
def is_operand(c):
return c.isalnum()
# Function to convert infix expression to postfix
def infix_to_postfix(expression):
stack = [] # Stack to store operators
postfix = [] # List to store the postfix expression
for char in expression:
if is_operand(char): # If it's an operand, add it to the result
postfix.append(char)
elif char == '(': # If it's '(', push it to the stack
stack.append(char)
elif char == ')': # If it's ')', pop until '(' is found
while stack and stack[-1] != '(':
postfix.append(stack.pop())
stack.pop() # Pop '(' from the stack
else: # If it's an operator
while (stack and precedence(stack[-1]) >= precedence(char)):
postfix.append(stack.pop()) # Pop operators with higher precedence
stack.append(char) # Push the current operator to the stack
# Pop any remaining operators from the stack
while stack:
postfix.append(stack.pop())
return ''.join(postfix) # Join the list into a string
# Input the infix expression
expression = input("Enter an infix expression: ")
# Convert the infix expression to postfix
postfix_expression = infix_to_postfix(expression)
# Output the postfix expression
print("Postfix expression:", postfix_expression)
3.Write a program to evaluate a postfix expression using stack.
Program:
Sure! Here's a simple Python program that evaluates a postfix expression using a stack:
Python Program:
def evaluate_postfix(expression):
stack = []
# Iterate through each character in the expression
for char in expression:
# If character is a number, push it to the stack
if char.isdigit():
stack.append(int(char))
# If character is an operator, pop two operands and apply the operator
else:
operand2 = stack.pop()
operand1 = stack.pop()
if char == '+':
stack.append(operand1 + operand2)
elif char == '-':
stack.append(operand1 - operand2)
elif char == '*':
stack.append(operand1 * operand2)
elif char == '/':
stack.append(operand1 / operand2)
# The result is the only item left in the stack
return stack[0]
# Example usage
postfix_expr = "23*5+"
result = evaluate_postfix(postfix_expr)
print(f"Result of postfix expression '{postfix_expr}' is: {result}")
Explanation:
We iterate over the postfix expression character by character.
If the character is a digit, we push it onto the stack.
If the character is an operator (+, -, *, /), we pop the top two elements from the stack, apply the
operator, and push the result back onto the stack.
Finally, the result of the postfix expression is the last remaining element in the stack.
Example Input & Output:
For the postfix expression "23*5+":
Step 1: 2, 3 are pushed onto the stack.
Step 2: * operator pops 2 and 3, multiplies them to get 6, then pushes 6 back onto the stack.
Step 3: 5 is pushed onto the stack.
Step 4: + operator pops 6 and 5, adds them to get 11, then pushes 11 back onto the stack.
Final result: 11
Output:
Result of postfix expression '23*5+' is: 11
Let me know if you'd like to try more examples or need further clarification!
4.Write a program to ensure the parentheses are nested correctly in an
arithmetic expression.
Program:
def check_parentheses(expression):
# Stack to keep track of opening parentheses
stack = []
# Traverse through the expression
for char in expression:
if char == '(':
# Push opening parenthesis onto stack
stack.append(char)
elif char == ')':
# If stack is empty, there's no matching opening parenthesis
if not stack:
return "Parentheses are not nested correctly."
# Pop the opening parenthesis from stack
stack.pop()
# If stack is not empty, there are unmatched opening parentheses
if stack:
return "Parentheses are not nested correctly."
return "Parentheses are nested correctly."
# Test the program
expression = "(3 + (4 * 5) - (6 / 2))"
result = check_parentheses(expression)
print(f"Expression: {expression}")
print(f"Result: {result}")
Explanation:
The program uses a list called stack to track the parentheses.
For each character in the expression:
o When an opening parenthesis ( is encountered, it’s pushed onto
the stack.
o When a closing parenthesis ) is encountered, the program checks
if there is a matching opening parenthesis by popping from the
stack.
o If there’s an unmatched closing parenthesis or an unmatched
opening parenthesis left in the stack at the end, the program
indicates that the parentheses are not nested correctly.
The expression is evaluated, and the result is displayed.
Sample Output:
Expression: (3 + (4 * 5) - (6 / 2))
Result: Parentheses are nested correctly.
If you change the expression to something with incorrect nesting, like ((3 + 4))),
the result will be:
Output:
Expression: ((3 + 4)))
Result: Parentheses are not nested correctly.
5.Write a program to find following using Recursion a) Factorial of +ve Integer
b) nth term of the Fibonacci Sequence (c) GCD of two positive integers
Program:
a) Factorial of +ve Integer
# Function to calculate factorial using recursion
def factorial(n):
# Base case: factorial of 0 or 1 is 1
if n == 0 or n == 1:
return 1
else:
# Recursive case: n * factorial of (n-1)
return n * factorial(n - 1)
# Test the function
n = 5 # Example positive integer
factorial_result = factorial(n)
# Output the result
print(f"Factorial of {n} is {factorial_result}")
Output:
Factorial of 5 is 120
b) nth term of the Fibonacci Sequence
# Function to calculate nth term of Fibonacci sequence using recursion
def fibonacci(n):
# Base case: first two terms of Fibonacci sequence are 0 and 1
if n == 0:
return 0
elif n == 1:
return 1
else:
# Recursive case: sum of the previous two Fibonacci numbers
return fibonacci(n - 1) + fibonacci(n - 2)
# Test the function
n = 6 # Example: find the 6th term in the Fibonacci sequence
fibonacci_result = fibonacci(n)
# Output the result
print(f"The {n}th term of the Fibonacci sequence is {fibonacci_result}")
Output: The 6th term of the Fibonacci sequence is 8
c) GCD of two positive integers
program:
# Function to calculate GCD using recursion (Euclidean algorithm)
def gcd(a, b):
# Base case: if b is 0, GCD is a
if b == 0:
return a
else:
# Recursive case: GCD of b and a % b
return gcd(b, a % b)
# Test the function
a = 56 # First positive integer
b = 98 # Second positive integer
gcd_result = gcd(a, b)
# Output the result
print(f"The GCD of {a} and {b} is {gcd_result}")
output: The GCD of 56 and 98 is 14
6.Write a program to create a single linked list and write functions to
implement the following operations. a) Insert an element at a specified
position b) Delete a specified element in the list c) Search for an element and
find its position in the list d) Sort the elements in the list ascending order
Answer:
a) Insert an element at a specified position
Program:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
# Insert an element at a specified position
def insert_at_position(self, data, position):
new_node = Node(data)
# If position is 0, insert at the beginning
if position == 0:
new_node.next = self.head
self.head = new_node
return
temp = self.head
# Traverse to the position-1 node (i.e., the node before the specified
position)
for i in range(position - 1):
if temp is None:
print("Position out of range")
return
temp = temp.next
# Insert the new node at the specified position
new_node.next = temp.next
temp.next = new_node
# Print the list elements
def print_list(self):
temp = self.head
if temp is None:
print("List is empty")
return
while temp:
print(temp.data, end=" -> ")
temp = temp.next
print("None")
# Example usage of the LinkedList class
# Creating the linked list
ll = LinkedList()
# Inserting elements at different positions
ll.insert_at_position(10, 0) # Insert 10 at position 0
ll.insert_at_position(20, 1) # Insert 20 at position 1
ll.insert_at_position(30, 2) # Insert 30 at position 2
ll.insert_at_position(25, 2) # Insert 25 at position 2
# Printing the list after insertions
print("List after inserting elements:")
ll.print_list()
output:
List after inserting elements:
10 -> 20 -> 25 -> 30 -> None
b) Delete a specified element in the list
Program:
class Node:
def __init__(self, data):
self.data = data # Stores the data of the node
self.next = None # Points to the next node in the list
class LinkedList:
def __init__(self):
self.head = None # Initializes an empty linked list
# Function to insert an element at the end of the linked list
def insert_at_end(self, data):
new_node = Node(data)
if self.head is None: # If the list is empty, make the new node the head
self.head = new_node
return
last_node = self.head
while last_node.next: # Traverse to the last node
last_node = last_node.next
last_node.next = new_node # Insert the new node at the end
# Function to delete a specified element from the list
def delete_element(self, key):
current = self.head
# If the element to delete is at the head of the list
if current and current.data == key:
self.head = current.next
current = None
return
# Traverse the list to find the element
prev = None
while current and current.data != key:
prev = current
current = current.next
# If the element was not found
if current is None:
print(f"Element {key} not found in the list.")
return
# Unlink the node from the list
prev.next = current.next
current = None
# Function to print the elements of the linked list
def print_list(self):
current = self.head
if current is None:
print("The list is empty.")
return
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage of the LinkedList class
ll = LinkedList()
# Inserting elements at the end
ll.insert_at_end(10)
ll.insert_at_end(20)
ll.insert_at_end(30)
ll.insert_at_end(40)
# Printing the list after insertions
print("List before deleting an element:")
ll.print_list()
# Deleting an element
ll.delete_element(30) # Delete element 30
# Printing the list after deletion
print("List after deleting element 30:")
ll.print_list()
# Trying to delete an element that doesn't exist
ll.delete_element(50) # Element 50 doesn't exist
output:
List before deleting an element:
10 -> 20 -> 30 -> 40 -> None
List after deleting element 30:
10 -> 20 -> 40 -> None
Element 50 not found in the list.
c) Search for an element and find its position in the list
Program:
class Node:
def __init__(self, data):
self.data = data # Stores the data of the node
self.next = None # Points to the next node in the list
class LinkedList:
def __init__(self):
self.head = None # Initializes an empty linked list
# Function to insert an element at the end of the linked list
def insert_at_end(self, data):
new_node = Node(data)
if self.head is None: # If the list is empty, make the new node the
head
self.head = new_node
return
last_node = self.head
while last_node.next: # Traverse to the last node
last_node = last_node.next
last_node.next = new_node # Insert the new node at the end
# Function to search for an element and return its position
def search_element(self, key):
current = self.head
position = 0
while current:
if current.data == key:
return position # Return the position if element is found
current = current.next
position += 1
return -1 # Return -1 if the element is not found
# Function to print the elements of the linked list
def print_list(self):
current = self.head
if current is None:
print("The list is empty.")
return
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage of the LinkedList class
ll = LinkedList()
# Inserting elements at the end
ll.insert_at_end(10)
ll.insert_at_end(20)
ll.insert_at_end(30)
ll.insert_at_end(40)
# Printing the list after insertions
print("List after inserting elements:")
ll.print_list()
# Searching for an element
search_key = 30
position = ll.search_element(search_key)
if position != -1:
print(f"Element {search_key} found at position {position}.")
else:
print(f"Element {search_key} not found in the list.")
# Searching for an element that doesn't exist
search_key = 50
position = ll.search_element(search_key)
if position != -1:
print(f"Element {search_key} found at position {position}.")
else:
print(f"Element {search_key} not found in the list.")
output:
List after inserting elements:
10 -> 20 -> 30 -> 40 -> None
Element 30 found at position 2.
Element 50 not found in the list.
d) Sort the elements in the list ascending order
program:
class Node:
def __init__(self, data):
self.data = data # Stores the data of the node
self.next = None # Points to the next node in the list
class LinkedList:
def __init__(self):
self.head = None # Initializes an empty linked list
# Function to insert an element at the end of the linked list
def insert_at_end(self, data):
new_node = Node(data)
if self.head is None: # If the list is empty, make the new node the
head
self.head = new_node
return
last_node = self.head
while last_node.next: # Traverse to the last node
last_node = last_node.next
last_node.next = new_node # Insert the new node at the end
# Function to sort the elements of the list in ascending order
def sort_list(self):
if self.head is None or self.head.next is None:
return # List is empty or has only one element, no need to sort
# Bubble Sort: Traverse the list and swap nodes if needed
current = self.head
while current:
index = current.next
while index:
if current.data > index.data: # If the current node is greater
than the next node
# Swap the data between the nodes
current.data, index.data = index.data, current.data
index = index.next
current = current.next
# Function to print the elements of the linked list
def print_list(self):
current = self.head
if current is None:
print("The list is empty.")
return
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage of the LinkedList class
ll = LinkedList()
# Inserting elements at the end
ll.insert_at_end(40)
ll.insert_at_end(10)
ll.insert_at_end(30)
ll.insert_at_end(20)
# Printing the list before sorting
print("List before sorting:")
ll.print_list()
# Sorting the list
ll.sort_list()
# Printing the list after sorting
print("List after sorting:")
ll.print_list()
output:
List before sorting:
40 -> 10 -> 30 -> 20 -> None
List after sorting:
10 -> 20 -> 30 -> 40 -> None
Or
class Node:
def __init__(self, data):
self.data = data
self.next = None
class SinglyLinkedList:
def __init__(self):
self.head = None
# Insert element at a specified position
def insert_at_position(self, data, position):
new_node = Node(data)
if position < 1:
print("Invalid position")
return
# Insert at the beginning
if position == 1:
new_node.next = self.head
self.head = new_node
return
# Traverse the list to find the position
current = self.head
count = 1
while current is not None and count < position - 1:
current = current.next
count += 1
if current is None:
print(f"Position {position} is out of bounds")
return
# Insert the new node at the given position
new_node.next = current.next
current.next = new_node
# Delete a specified element
def delete_element(self, data):
if self.head is None:
print("List is empty")
return
# If the element to be deleted is the head
if self.head.data == data:
self.head = self.head.next
return
# Traverse the list to find the element
current = self.head
while current.next is not None and current.next.data != data:
current = current.next
if current.next is None:
print(f"Element {data} not found")
return
# Delete the element
current.next = current.next.next
# Search for an element and find its position
def search(self, data):
current = self.head
position = 1
while current is not None:
if current.data == data:
return position
current = current.next
position += 1
return -1 # Element not found
# Sort the elements in ascending order
def sort_list(self):
if self.head is None:
return
# Bubble sort algorithm to sort the list
end = None
while end != self.head:
current = self.head
while current.next != end:
if current.data > current.next.data:
current.data, current.next.data = current.next.data,
current.data
current = current.next
end = current
# Display the elements of the list
def display(self):
if self.head is None:
print("List is empty")
return
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Test the Singly Linked List
if __name__ == "__main__":
sll = SinglyLinkedList()
# Insert elements at different positions
sll.insert_at_position(10, 1)
sll.insert_at_position(20, 2)
sll.insert_at_position(15, 2)
sll.insert_at_position(30, 4)
print("Linked List after insertions:")
sll.display()
# Delete an element
sll.delete_element(15)
print("Linked List after deleting 15:")
sll.display()
# Search for an element
position = sll.search(20)
if position != -1:
print(f"Element 20 found at position: {position}")
else:
print("Element 20 not found")
# Sort the list
sll.sort_list()
print("Linked List after sorting:")
sll.display()
output:
Linked List after insertions:
10 -> 15 -> 20 -> 30 -> None
Linked List after deleting 15:
10 -> 20 -> 30 -> None
Element 20 found at position: 2
Linked List after sorting:
10 -> 20 -> 30 -> None
7.Write a program to create a double linked list and write functions to
implement the following operations. a) Insert an element at a specified
position b) Delete a specified element in the list c) Search for an element and
find its position in the list d) Sort the elements in the list ascending order
a) Insert an element at a specified position
program:
class Node:
def __init__(self, data):
self.data = data # The data of the node
self.next = None # Pointer to the next node
self.prev = None # Pointer to the previous node
class DoublyLinkedList:
def __init__(self):
self.head = None # Initially, the list is empty
def print_list(self):
# Function to print the elements of the list
temp = self.head
while temp:
print(temp.data, end=" <=> ")
temp = temp.next
print("None")
def insert_at_position(self, data, position):
new_node = Node(data)
# Inserting at the head (position 0)
if position == 0:
new_node.next = self.head
if self.head:
self.head.prev = new_node
self.head = new_node
return
# Traverse to the position
temp = self.head
count = 0
while temp and count < position - 1:
temp = temp.next
count += 1
# If the position is valid (node found)
if temp:
new_node.next = temp.next
if temp.next:
temp.next.prev = new_node
temp.next = new_node
new_node.prev = temp
def delete_element(self, data):
temp = self.head
# Traverse the list to find the element to delete
while temp and temp.data != data:
temp = temp.next
# Element not found
if not temp:
print(f"Element {data} not found in the list")
return
# If the element to be deleted is the head
if temp == self.head:
self.head = temp.next
if self.head:
self.head.prev = None
temp = None
return
# If the element is in the middle or end
temp.prev.next = temp.next
if temp.next:
temp.next.prev = temp.prev
temp = None
# Example usage:
dll = DoublyLinkedList()
# Insert elements
dll.insert_at_position(10, 0) # Insert 10 at position 0
dll.insert_at_position(20, 1) # Insert 20 at position 1
dll.insert_at_position(15, 1) # Insert 15 at position 1
dll.insert_at_position(5, 0) # Insert 5 at position 0
dll.insert_at_position(25, 3) # Insert 25 at position 3
print("List after insertion:")
dll.print_list() # Output the list
output:
List after insertion:
5 <=> 10 <=> 15 <=> 20 <=> 25 <=> None
b) Delete a specified element in the list
class Node:
def __init__(self, data):
self.data = data # The data of the node
self.next = None # Pointer to the next node
self.prev = None # Pointer to the previous node
class DoublyLinkedList:
def __init__(self):
self.head = None # Initially, the list is empty
def print_list(self):
# Function to print the elements of the list
temp = self.head
while temp:
print(temp.data, end=" <=> ")
temp = temp.next
print("None")
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def delete_element(self, data):
temp = self.head
# If the list is empty
if not temp:
print("The list is empty!")
return
# If the element to be deleted is the head node
if temp.data == data:
self.head = temp.next
if self.head:
self.head.prev = None
temp = None
return
# Traverse the list to find the element to delete
while temp and temp.data != data:
temp = temp.next
# Element not found
if not temp:
print(f"Element {data} not found in the list")
return
# If the element is in the middle or end
temp.prev.next = temp.next
if temp.next:
temp.next.prev = temp.prev
temp = None
# Example usage:
dll = DoublyLinkedList()
# Insert elements
dll.insert_at_end(10)
dll.insert_at_end(20)
dll.insert_at_end(30)
dll.insert_at_end(40)
dll.insert_at_end(50)
print("List before deletion:")
dll.print_list() # Output the list
# Delete an element
dll.delete_element(30)
print("\nList after deleting 30:")
dll.print_list() # Output the list after deletion
# Attempt to delete an element not in the list
dll.delete_element(60)
output:
List before deletion:
10 <=> 20 <=> 30 <=> 40 <=> 50 <=> None
List after deleting 30:
10 <=> 20 <=> 40 <=> 50 <=> None
Element 60 not found in the list
c) Search for an element and find its position in the list
program:
class Node:
def __init__(self, data):
self.data = data # Data of the node
self.next = None # Pointer to the next node
self.prev = None # Pointer to the previous node
class DoublyLinkedList:
def __init__(self):
self.head = None # Initially, the list is empty
def print_list(self):
# Function to print the elements of the list
temp = self.head
while temp:
print(temp.data, end=" <=> ")
temp = temp.next
print("None")
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def search_element(self, data):
temp = self.head
position = 0
while temp:
if temp.data == data:
print(f"Element {data} found at position {position}")
return position
temp = temp.next
position += 1
print(f"Element {data} not found in the list")
return -1
# Example usage:
dll = DoublyLinkedList()
# Insert elements
dll.insert_at_end(10)
dll.insert_at_end(20)
dll.insert_at_end(30)
dll.insert_at_end(40)
dll.insert_at_end(50)
print("List after insertions:")
dll.print_list() # Output the list
# Search for an element
dll.search_element(30) # Search for element 30
dll.search_element(60) # Search for an element that doesn't exist
output:
List after insertions:
10 <=> 20 <=> 30 <=> 40 <=> 50 <=> None
Element 30 found at position 2
Element 60 not found in the list
d) Sort the elements in the list ascending order
program:
class Node:
def __init__(self, data):
self.data = data # The data of the node
self.next = None # Pointer to the next node
self.prev = None # Pointer to the previous node
class DoublyLinkedList:
def __init__(self):
self.head = None # Initially, the list is empty
def print_list(self):
# Function to print the elements of the list
temp = self.head
while temp:
print(temp.data, end=" <=> ")
temp = temp.next
print("None")
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def sort_list(self):
if not self.head or not self.head.next:
return # List is empty or has one element, no need to sort
# Bubble sort method to sort the list
swapped = True
while swapped:
swapped = False
temp = self.head
while temp and temp.next:
if temp.data > temp.next.data:
# Swap the data of the nodes
temp.data, temp.next.data = temp.next.data, temp.data
swapped = True
temp = temp.next
# Example usage:
dll = DoublyLinkedList()
# Insert elements
dll.insert_at_end(40)
dll.insert_at_end(10)
dll.insert_at_end(30)
dll.insert_at_end(20)
dll.insert_at_end(50)
print("List before sorting:")
dll.print_list() # Output the list
# Sort the list
dll.sort_list()
print("\nList after sorting in ascending order:")
dll.print_list() # Output the sorted list
output:
List before sorting:
40 <=> 10 <=> 30 <=> 20 <=> 50 <=> None
List after sorting in ascending order:
10 <=> 20 <=> 30 <=> 40 <=> 50 <=> None
8.Write a program to create singular circular linked lists and function to
implement the following operations. a) Insert an element at a specified
position b) Delete a specified element in the list c) Search for an element and
find its position in the list
a)Insert an element at a specified position
Program:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def insert_at_position(self, data, position):
new_node = Node(data)
# If the list is empty
if not self.head:
new_node.next = new_node
self.head = new_node
print(f"Inserted {data} as the first node.")
return
# Insert at position 0 (head)
if position == 0:
tail = self.head
while tail.next != self.head:
tail = tail.next
new_node.next = self.head
tail.next = new_node
self.head = new_node
print(f"Inserted {data} at position {position}.")
return
# Insert at any other position
current = self.head
count = 0
while count < position - 1 and current.next != self.head:
current = current.next
count += 1
if count != position - 1:
print("Position out of bounds.")
return
new_node.next = current.next
current.next = new_node
print(f"Inserted {data} at position {position}.")
def display(self):
if not self.head:
print("List is empty.")
return
current = self.head
print("Circular Linked List:", end=" ")
while True:
print(current.data, end=" -> ")
current = current.next
if current == self.head:
break
print("(back to head)")
# Example usage
cll = CircularLinkedList()
cll.insert_at_position(10, 0)
cll.insert_at_position(20, 1)
cll.insert_at_position(15, 1)
cll.display()
output:
Inserted 10 as the first node.
Inserted 20 at position 1.
Inserted 15 at position 1.
Circular Linked List: 10 -> 15 -> 20 -> (back to head)
b)Delete a specified element in the list
Program:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def insert_at_position(self, data, position):
new_node = Node(data)
if not self.head:
new_node.next = new_node
self.head = new_node
print(f"Inserted {data} as the first node.")
return
if position == 0:
tail = self.head
while tail.next != self.head:
tail = tail.next
new_node.next = self.head
tail.next = new_node
self.head = new_node
print(f"Inserted {data} at position {position}.")
return
current = self.head
count = 0
while count < position - 1 and current.next != self.head:
current = current.next
count += 1
if count != position - 1:
print("Position out of bounds.")
return
new_node.next = current.next
current.next = new_node
print(f"Inserted {data} at position {position}.")
def delete_element(self, data):
if not self.head:
print("List is empty.")
return
current = self.head
prev = None
# Deleting the head node
if current.data == data:
if current.next == self.head: # Only one node present
self.head = None
else:
tail = self.head
while tail.next != self.head:
tail = tail.next
tail.next = current.next
self.head = current.next
print(f"Deleted {data} from the list.")
return
# Deleting from other positions
while True:
prev = current
current = current.next
if current.data == data:
prev.next = current.next
print(f"Deleted {data} from the list.")
return
if current == self.head:
break
print(f"Element {data} not found in the list.")
def display(self):
if not self.head:
print("List is empty.")
return
current = self.head
print("Circular Linked List:", end=" ")
while True:
print(current.data, end=" -> ")
current = current.next
if current == self.head:
break
print("(back to head)")
# Example usage
cll = CircularLinkedList()
cll.insert_at_position(10, 0)
cll.insert_at_position(20, 1)
cll.insert_at_position(15, 1)
cll.display()
# Deleting elements
cll.delete_element(15)
cll.display()
cll.delete_element(10)
cll.display()
cll.delete_element(100) # Element not in the list
Output:
Inserted 10 as the first node.
Inserted 20 at position 1.
Inserted 15 at position 1.
Circular Linked List: 10 -> 15 -> 20 -> (back to head)
Deleted 15 from the list.
Circular Linked List: 10 -> 20 -> (back to head)
Deleted 10 from the list.
Circular Linked List: 20 -> (back to head)
Element 100 not found in the list.
c) Search for an element and find its position in the list
Program:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def insert_at_position(self, data, position):
new_node = Node(data)
if not self.head:
new_node.next = new_node
self.head = new_node
print(f"Inserted {data} as the first node.")
return
if position == 0:
tail = self.head
while tail.next != self.head:
tail = tail.next
new_node.next = self.head
tail.next = new_node
self.head = new_node
print(f"Inserted {data} at position {position}.")
return
current = self.head
count = 0
while count < position - 1 and current.next != self.head:
current = current.next
count += 1
if count != position - 1:
print("Position out of bounds.")
return
new_node.next = current.next
current.next = new_node
print(f"Inserted {data} at position {position}.")
def search_element(self, data):
if not self.head:
print("List is empty.")
return -1
current = self.head
position = 0
while True:
if current.data == data:
print(f"Element {data} found at position {position}.")
return position
current = current.next
position += 1
if current == self.head:
break
print(f"Element {data} not found in the list.")
return -1
def display(self):
if not self.head:
print("List is empty.")
return
current = self.head
print("Circular Linked List:", end=" ")
while True:
print(current.data, end=" -> ")
current = current.next
if current == self.head:
break
print("(back to head)")
# Example usage
cll = CircularLinkedList()
cll.insert_at_position(10, 0)
cll.insert_at_position(20, 1)
cll.insert_at_position(15, 1)
cll.display()
# Searching for elements
cll.search_element(15)
cll.search_element(10)
cll.search_element(100) # Element not in the list
output:
Inserted 10 as the first node.
Inserted 20 at position 1.
Inserted 15 at position 1.
Circular Linked List: 10 -> 15 -> 20 -> (back to head)
Element 15 found at position 1.
Element 10 found at position 0.
Element 100 not found in the list.
9.Write programs to implement the following using a single linked list: a)
Stack ADT b) Queue ADT.
Program
Sure! Here's the complete code with output for both:
a) Stack ADT using Singly Linked List
# Node class
class Node:
def __init__(self, data):
self.data = data
self.next = None
# Stack class
class Stack:
def __init__(self):
self.top = None
def push(self, data):
new_node = Node(data)
new_node.next = self.top
self.top = new_node
print(f"Pushed: {data}")
def pop(self):
if self.top is None:
print("Stack Underflow")
return
popped = self.top.data
self.top = self.top.next
print(f"Popped: {popped}")
def display(self):
current = self.top
print("Stack from top to bottom:")
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage
stack = Stack()
stack.push(10)
stack.push(20)
stack.push(30)
stack.display()
stack.pop()
stack.display()
Output:
Pushed: 10
Pushed: 20
Pushed: 30
Stack from top to bottom:
30 -> 20 -> 10 -> None
Popped: 30
Stack from top to bottom:
20 -> 10 -> None
b) Queue ADT using Singly Linked List
# Node class
class Node:
def __init__(self, data):
self.data = data
self.next = None
# Queue class
class Queue:
def __init__(self):
self.front = None
self.rear = None
def enqueue(self, data):
new_node = Node(data)
if self.rear is None:
self.front = self.rear = new_node
else:
self.rear.next = new_node
self.rear = new_node
print(f"Enqueued: {data}")
def dequeue(self):
if self.front is None:
print("Queue Underflow")
return
dequeued = self.front.data
self.front = self.front.next
if self.front is None:
self.rear = None
print(f"Dequeued: {dequeued}")
def display(self):
current = self.front
print("Queue from front to rear:")
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage
queue = Queue()
queue.enqueue(100)
queue.enqueue(200)
queue.enqueue(300)
queue.display()
queue.dequeue()
queue.display()
Output:
Enqueued: 100
Enqueued: 200
Enqueued: 300
Queue from front to rear:
100 -> 200 -> 300 -> None
Dequeued: 100
Queue from front to rear:
200 -> 300 -> None
10 a). Write a program to implement Binary search technique using Iterative
method and Recursive methods.
Program:
Sure! Here's a simple Python program that implements Binary Search using
both Iterative and Recursive methods, along with example usage and output.
Binary Search in Python
1️. iterative Method
2️. Recursive Method
Code:
# Binary Search - Iterative Method
def binary_search_iterative(arr, target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid # Element found
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1 # Element not found
# Binary Search - Recursive Method
def binary_search_recursive(arr, low, high, target):
if low > high:
return -1 # Element not found
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search_recursive(arr, mid + 1, high, target)
else:
return binary_search_recursive(arr, low, mid - 1, target)
# Example usage
arr = [10, 20, 30, 40, 50, 60, 70]
target = 40
# Iterative
print("Using Iterative Method:")
index_iter = binary_search_iterative(arr, target)
if index_iter != -1:
print(f"Element {target} found at index {index_iter}")
else:
print(f"Element {target} not found")
# Recursive
print("\nUsing Recursive Method:")
index_rec = binary_search_recursive(arr, 0, len(arr) - 1, target)
if index_rec != -1:
print(f"Element {target} found at index {index_rec}")
else:
print(f"Element {target} not found")
Output:
Using Iterative Method:
Element 40 found at index 3
Using Recursive Method:
Element 40 found at index 3
10 b). Write a program for sorting the given list numbers in ascending order
using the following technique: Bubble sort and Selection sort
Program:
Sorting in Python:
1️⃣ Bubble Sort
2️⃣ Selection Sort
Code:
# Bubble Sort Function
def bubble_sort(arr):
n = len(arr)
for i in range(n - 1): # Number of passes
for j in range(n - 1 - i): # Pairwise comparison
if arr[j] > arr[j + 1]: # Swap if out of order
arr[j], arr[j + 1] = arr[j + 1], arr[j]
# Selection Sort Function
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_index = i
for j in range(i + 1, n):
if arr[j] < arr[min_index]:
min_index = j
# Swap the found minimum with the first element
arr[i], arr[min_index] = arr[min_index], arr[i]
# Example list
numbers1 = [64, 25, 12, 22, 11]
numbers2 = numbers1.copy() # For second sort
# Bubble Sort
print("Original List:", numbers1)
bubble_sort(numbers1)
print("Sorted using Bubble Sort:", numbers1)
# Selection Sort
print("\nOriginal List:", numbers2)
selection_sort(numbers2)
print("Sorted using Selection Sort:", numbers2)
Output:
Original List: [64, 25, 12, 22, 11]
Sorted using Bubble Sort: [11, 12, 22, 25, 64]
Original List: [64, 25, 12, 22, 11]
Sorted using Selection Sort: [11, 12, 22, 25, 64]
11.Write a program for sorting the given list numbers in ascending order
using the following technique: Insertion sort and Quicksort
Programs:
1) Insertion Sort
2) Quick Sort
Code:
# Insertion Sort Function
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j=i-1
# Move elements of arr[0..i-1] that are greater than key
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
# Quick Sort Function
def quick_sort(arr):
if len(arr) <= 1:
return arr
else:
pivot = arr[0] # Choosing the first element as pivot
less = [x for x in arr[1:] if x <= pivot]
greater = [x for x in arr[1:] if x > pivot]
return quick_sort(less) + [pivot] + quick_sort(greater)
# Example list
numbers1 = [29, 10, 14, 37, 13]
numbers2 = numbers1.copy()
# Insertion Sort
print("Original List:", numbers1)
insertion_sort(numbers1)
print("Sorted using Insertion Sort:", numbers1)
# Quick Sort
print("\nOriginal List:", numbers2)
sorted_quick = quick_sort(numbers2)
print("Sorted using Quick Sort:", sorted_quick)
Output:
Original List: [29, 10, 14, 37, 13]
Sorted using Insertion Sort: [10, 13, 14, 29, 37]
Original List: [29, 10, 14, 37, 13]
Sorted using Quick Sort: [10, 13, 14, 29, 37]
12.Write a program for sorting the given list numbers in ascending order
using the following technique: Merge sort and Heapsort
Program:
1) Merge Sort
2) Heap Sort
Code:
# Merge Sort Function
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
merge_sort(left_half)
merge_sort(right_half)
# Merge the sorted halves
i=j=k=0
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1
# Copy remaining elements
while i < len(left_half):
arr[k] = left_half[i]
i += 1
k += 1
while j < len(right_half):
arr[k] = right_half[j]
j += 1
k += 1
# Heap Sort Function
def heapify(arr, n, i):
largest = i # Initialize largest as root
left = 2 * i + 1 # left child index
right = 2 * i + 2 # right child index
# Check if left child is larger
if left < n and arr[left] > arr[largest]:
largest = left
# Check if right child is larger
if right < n and arr[right] > arr[largest]:
largest = right
# If root is not largest, swap and heapify
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort(arr):
n = len(arr)
# Build max heap
for i in range(n//2 - 1, -1, -1):
heapify(arr, n, i)
# Extract elements from heap
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # Swap
heapify(arr, i, 0)
# Example list
numbers1 = [45, 23, 11, 89, 77, 98, 4]
numbers2 = numbers1.copy()
# Merge Sort
print("Original List:", numbers1)
merge_sort(numbers1)
print("Sorted using Merge Sort:", numbers1)
# Heap Sort
print("\nOriginal List:", numbers2)
heap_sort(numbers2)
print("Sorted using Heap Sort:", numbers2)
Output:
Original List: [45, 23, 11, 89, 77, 98, 4]
Sorted using Merge Sort: [4, 11, 23, 45, 77, 89, 98]
Original List: [45, 23, 11, 89, 77, 98, 4]
Sorted using Heap Sort: [4, 11, 23, 45, 77, 89, 98]
13.Write a program to traverse a binary tree in following way. a) Pre-order b)
In-order c)Post-order
Program:
Pre-order
In-order
Post-order
Code:
# Define a Node class for Binary Tree
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
# Pre-order Traversal (Root, Left, Right)
def preorder(root):
if root:
print(root.data, end=" ") # Visit root
preorder(root.left) # Visit left subtree
preorder(root.right) # Visit right subtree
# In-order Traversal (Left, Root, Right)
def inorder(root):
if root:
inorder(root.left) # Visit left subtree
print(root.data, end=" ") # Visit root
inorder(root.right) # Visit right subtree
# Post-order Traversal (Left, Right, Root)
def postorder(root):
if root:
postorder(root.left) # Visit left subtree
postorder(root.right) # Visit right subtree
print(root.data, end=" ") # Visit root
# Example of binary tree:
# 1
# / \
# 2 3
# /\ /\
# 4 56 7
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.right.left = Node(6)
root.right.right = Node(7)
# Pre-order Traversal
print("Pre-order Traversal:")
preorder(root) # Output: 1 2 4 5 3 6 7
# In-order Traversal
print("\nIn-order Traversal:")
inorder(root) # Output: 4 2 5 1 6 3 7
# Post-order Traversal
print("\nPost-order Traversal:")
postorder(root) # Output: 4 5 2 6 7 3 1
Output:
Pre-order Traversal:
1245367
In-order Traversal:
4251637
Post-order Traversal:
4526731
14.Write a program to find the minimum spanning tree for a weighted graph
using a) Prim’s Algorithm b) Kruskal’s Algorithm.
Program:
a) Prim’s Algorithm
b) Kruskal’s Algorithm
a) Prim’s Algorithm (Using Adjacency Matrix)
import sys
# Prim's Algorithm for MST using adjacency matrix
def prims_algorithm(graph):
num_vertices = len(graph)
selected = [False] * num_vertices
keys = [sys.maxsize] * num_vertices
parent = [None] * num_vertices
keys[0] = 0 # Start from vertex 0
for _ in range(num_vertices):
# Find the minimum key vertex not yet included
min_key = sys.maxsize
for v in range(num_vertices):
if not selected[v] and keys[v] < min_key:
min_key = keys[v]
u=v
selected[u] = True
# Update key and parent for adjacent vertices
for v in range(num_vertices):
if graph[u][v] and not selected[v] and keys[v] > graph[u][v]:
keys[v] = graph[u][v]
parent[v] = u
# Print MST
print("Prim's MST:")
total_weight = 0
for i in range(1, num_vertices):
print(f"{parent[i]} - {i} : {graph[i][parent[i]]}")
total_weight += graph[i][parent[i]]
print("Total Weight:", total_weight)
# Example graph (Adjacency Matrix)
graph = [
[0, 2, 0, 6, 0],
[2, 0, 3, 8, 5],
[0, 3, 0, 0, 7],
[6, 8, 0, 0, 9],
[0, 5, 7, 9, 0]
]
prims_algorithm(graph)
Output (Prim’s):
Prim's MST:
0-1:2
1-2:3
0-3:6
1-4:5
Total Weight: 16
b) Kruskal’s Algorithm (Using Edge List + Union-Find)
# Kruskal's Algorithm for MST
# Disjoint Set (Union-Find)
class DisjointSet:
def __init__(self, vertices):
self.parent = {v: v for v in vertices}
def find(self, v):
if self.parent[v] != v:
self.parent[v] = self.find(self.parent[v]) # Path compression
return self.parent[v]
def union(self, u, v):
root1 = self.find(u)
root2 = self.find(v)
if root1 != root2:
self.parent[root2] = root1
return True
return False
def kruskal_algorithm(vertices, edges):
ds = DisjointSet(vertices)
mst = []
total_weight = 0
# Sort edges by weight
edges.sort(key=lambda edge: edge[2])
for u, v, weight in edges:
if ds.union(u, v):
mst.append((u, v, weight))
total_weight += weight
# Print MST
print("\nKruskal's MST:")
for u, v, weight in mst:
print(f"{u} - {v} : {weight}")
print("Total Weight:", total_weight)
# Example graph (Edge list)
vertices = ['A', 'B', 'C', 'D', 'E']
edges = [
('A', 'B', 2),
('A', 'D', 6),
('B', 'C', 3),
('B', 'D', 8),
('B', 'E', 5),
('C', 'E', 7),
('D', 'E', 9)
]
kruskal_algorithm(vertices, edges)
(Kruskal’s):
Kruskal's MST:
A-B:2
B-C:3
B-E:5
A-D:6
Total Weight: 16
15 Write a program to the implementation graph traversals – BFS and DFS.
Program:
1) Breadth-First Search (BFS)
2) Depth-First Search (DFS)
Code:
from collections import deque
# Graph class
class Graph:
def __init__(self):
self.graph = {}
# Add an edge (undirected)
def add_edge(self, u, v):
if u not in self.graph:
self.graph[u] = []
if v not in self.graph:
self.graph[v] = []
self.graph[u].append(v)
self.graph[v].append(u)
# Breadth-First Search (BFS)
def bfs(self, start):
visited = set()
queue = deque([start])
print("BFS Traversal:", end=" ")
while queue:
node = queue.popleft()
if node not in visited:
print(node, end=" ")
visited.add(node)
for neighbor in self.graph[node]:
if neighbor not in visited:
queue.append(neighbor)
# Depth-First Search (DFS)
def dfs(self, start):
visited = set()
print("DFS Traversal:", end=" ")
self._dfs_recursive(start, visited)
def _dfs_recursive(self, node, visited):
if node not in visited:
print(node, end=" ")
visited.add(node)
for neighbor in self.graph[node]:
if neighbor not in visited:
self._dfs_recursive(neighbor, visited)
# Example graph:
# A
# /\
# B C
# /\ \
#D E F
# Create graph instance
g = Graph()
g.add_edge('A', 'B')
g.add_edge('A', 'C')
g.add_edge('B', 'D')
g.add_edge('B', 'E')
g.add_edge('C', 'F')
# Perform traversals
g.bfs('A') # Expected BFS: A B C D E F
print()
g.dfs('A') # Expected DFS: A B D E C F
Output:
BFS Traversal: A B C D E F
DFS Traversal: A B D E C F