0% found this document useful (0 votes)
2 views71 pages

1-Data Stucture Using Python Lab

The document provides a series of Python programs demonstrating data structures and algorithms, including stack and queue implementations, infix to postfix expression conversion, postfix expression evaluation, parentheses checking, recursion for factorial, Fibonacci, and GCD, as well as linked list operations. Each section includes code snippets, explanations, and example outputs. The focus is on practical applications of data structures using Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views71 pages

1-Data Stucture Using Python Lab

The document provides a series of Python programs demonstrating data structures and algorithms, including stack and queue implementations, infix to postfix expression conversion, postfix expression evaluation, parentheses checking, recursion for factorial, Fibonacci, and GCD, as well as linked list operations. Each section includes code snippets, explanations, and example outputs. The focus is on practical applications of data structures using Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 71

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

You might also like