1.
Implement binary search to find the position of a target element in a
sorted array.
def binary_search(arr, target):
# Set the initial low and high pointers
low = 0
high = len(arr) - 1
# Iterate while the low pointer is less than or equal to the high pointer
while low <= high:
# Find the middle index
mid = (low + high) // 2
# If the target element is found, return its index
if arr[mid] == target:
return mid
# If the target is less than the middle element, search the left half
elif arr[mid] > target:
high = mid - 1
# If the target is greater than the middle element, search the right half
else:
low = mid + 1
# If the target element is not found, return -1
return -1
# Example usage:
arr = [1, 3, 5, 7, 9, 11, 13, 15]
target = 7
result = binary_search(arr, target)
if result != -1:
print(f"Element {target} is at index {result}.")
else:
print(f"Element {target} is not in the array.")
O/p:
[1, 3, 5, 7, 9, 11, 13, 15]
2. Write a program to sort a list using the bubble sort algorithm.
def bubble_sort(arr):
n = len(arr)
# Traverse through all elements in the list
for i in range(n):
# Last i elements are already sorted, so no need to check them again
swapped = False # Flag to check if any swap happened
for j in range(0, n-i-1):
# Compare adjacent elements
if arr[j] > arr[j+1]:
# Swap if the element is greater than the next element
arr[j], arr[j+1] = arr[j+1], arr[j]
swapped = True
# If no elements were swapped, the list is already sorted
if not swapped:
break
return arr
# Example usage:
arr = [64, 34, 25, 12, 22, 11, 90]
sorted_arr = bubble_sort(arr)
print("Sorted array is:", sorted_arr)
O/p:
Sorted array is: [11, 12, 22, 25, 34, 64, 90]
3. Implement a queue using a list in Python.
class Queue:
def __init__(self):
[Link] = [] # Initialize an empty list to store queue elements
def enqueue(self, item):
"""Add an item to the rear of the queue"""
[Link](item)
def dequeue(self):
"""Remove and return the item from the front of the queue"""
if self.is_empty():
return "Queue is empty"
return [Link](0)
def peek(self):
"""Return the item at the front of the queue without removing it"""
if self.is_empty():
return "Queue is empty"
return [Link][0]
def is_empty(self):
"""Check if the queue is empty"""
return len([Link]) == 0
def size(self):
"""Return the number of elements in the queue"""
return len([Link])
# Example usage:
queue = Queue()
# Enqueue elements
[Link](10)
[Link](20)
[Link](30)
# Display the queue after enqueuing elements
print("Queue after enqueuing 10, 20, 30:", [Link])
# Dequeue an element
print("Dequeue operation result:", [Link]()) # Should remove
10
print("Queue after dequeue:", [Link])
# Peek the front element
print("Peek operation result:", [Link]()) # Should show 20
# Check if the queue is empty
print("Is the queue empty?", queue.is_empty()) # Should return False
# Get the size of the queue
print("Queue size:", [Link]()) # Should return 2
O/p:
Queue after enqueuing 10, 20, 30: [10, 20, 30]
Dequeue operation result: 10
Queue after dequeue: [20, 30]
Peek operation result: 20
Is the queue empty? False
Queue size: 2
4. Implement a stack data structure with push, pop, and
display operations.
class Stack:
def __init__(self):
[Link] = [] # Initialize an empty list to store stack elements
def push(self, item):
"""Add an item to the top of the stack"""
[Link](item)
def pop(self):
"""Remove and return the item from the top of the stack"""
if self.is_empty():
return "Stack is empty"
return [Link]()
def display(self):
"""Display the current elements in the stack"""
if self.is_empty():
return "Stack is empty"
return [Link]
def is_empty(self):
"""Check if the stack is empty"""
return len([Link]) == 0
# Example usage:
stack = Stack()
# Push elements onto the stack
[Link](10)
[Link](20)
[Link](30)
# Display the stack after pushing elements
print("Stack after pushing 10, 20, 30:", [Link]())
# Pop an element from the stack
print("Pop operation result:", [Link]()) # Should remove 30
print("Stack after pop:", [Link]())
# Display the stack content
print("Current stack:", [Link]())
# Check if the stack is empty
print("Is the stack empty?", stack.is_empty()) # Should return False
# Pop remaining elements
print("Pop operation result:", [Link]()) # Should remove 20
print("Pop operation result:", [Link]()) # Should remove 10
# Check if the stack is empty now
print("Is the stack empty now?", stack.is_empty()) # Should return True
O/p:
Stack after pushing 10, 20, 30: [10, 20, 30]
Pop operation result: 30
Stack after pop: [10, 20]
Current stack: [10, 20]
Is the stack empty? False
Pop operation result: 20
Pop operation result: 10
Is the stack empty now? True
5. Write a program to merge two sorted arrays into a single
sorted array
def merge_sorted_arrays(arr1, arr2):
# Initialize pointers for both arrays and the result array
i, j = 0, 0
merged_arr = []
# Traverse both arrays and merge them
while i < len(arr1) and j < len(arr2):
if arr1[i] < arr2[j]:
merged_arr.append(arr1[i])
i += 1
else:
merged_arr.append(arr2[j])
j += 1
# If there are remaining elements in arr1, append them
while i < len(arr1):
merged_arr.append(arr1[i])
i += 1
# If there are remaining elements in arr2, append them
while j < len(arr2):
merged_arr.append(arr2[j])
j += 1
return merged_arr
# Example usage:
arr1 = [1, 3, 5, 7]
arr2 = [2, 4, 6, 8]
merged_array = merge_sorted_arrays(arr1, arr2)
print("Merged sorted array:", merged_array)
O/p:
Merged sorted array: [1, 2, 3, 4, 5, 6, 7, 8]
6. Write a program to find the shortest path in a graph using
Dijkstra's algorithm.
import heapq
def dijkstra(graph, start):
# Initialize distances from start to all other nodes as infinity
# and distance to start node as 0
distances = {node: float('inf') for node in graph}
distances[start] = 0
# Priority queue to select the node with the smallest tentative
distance
pq = [(0, start)] # (distance, node)
while pq:
# Get the node with the smallest distance
current_distance, current_node = [Link](pq)
# Skip if we've already found a better way to this node
if current_distance > distances[current_node]:
continue
# Explore the neighbors of the current node
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
# If the newly calculated distance is shorter, update it
if distance < distances[neighbor]:
distances[neighbor] = distance
[Link](pq, (distance, neighbor))
return distances
# Example graph
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
# Start from node 'A'
start_node = 'A'
shortest_paths = dijkstra(graph, start_node)
# Print shortest distances from the start node to all other nodes
print("Shortest paths from node", start_node)
for node, distance in shortest_paths.items():
print(f"Distance from {start_node} to {node} is {distance}")
O/p:
Shortest paths from node A
Distance from A to A is 0
Distance from A to B is 1
Distance from A to C is 3
Distance from A to D is 4
7. Implement a linked list with insertion and deletion operations.
class Node:
def __init__(self, data):
[Link] = data # The value of the node
[Link] = None # Pointer to the next node
class LinkedList:
def __init__(self):
[Link] = None # Initially, the list is empty
# Insert at the end of the list
def insert_end(self, data):
new_node = Node(data)
if [Link] is None:
[Link] = new_node # If the list is empty, new node becomes
the head
return
last = [Link]
while [Link]:
last = [Link]
[Link] = new_node
# Insert at the beginning of the list
def insert_begin(self, data):
new_node = Node(data)
new_node.next = [Link]
[Link] = new_node
# Delete a node with a specific value
def delete_node(self, key):
temp = [Link]
# If the node to be deleted is the head node
if temp is not None and [Link] == key:
[Link] = [Link]
temp = None
return
# Search for the node to be deleted
prev = None
while temp is not None and [Link] != key:
prev = temp
temp = [Link]
# If the key was not found
if temp is None:
print(f"Node with value {key} not found.")
return
# Unlink the node from the linked list
[Link] = [Link]
temp = None
# Print the linked list
def display(self):
if [Link] is None:
print("List is empty.")
return
current = [Link]
while current:
print([Link], end=" -> ")
current = [Link]
print("None")
# Example usage
linked_list = LinkedList()
# Insert elements
linked_list.insert_end(10)
linked_list.insert_end(20)
linked_list.insert_end(30)
print("Linked List after insertion at the end:")
linked_list.display()
linked_list.insert_begin(5)
print("Linked List after insertion at the beginning:")
linked_list.display()
# Delete a node with value 20
linked_list.delete_node(20)
print("Linked List after deleting node with value 20:")
linked_list.display()
# Delete a node that does not exist
linked_list.delete_node(50)
O/p:
Linked List after insertion at the end:
10 -> 20 -> 30 -> None
Linked List after insertion at the beginning:
5 -> 10 -> 20 -> 30 -> None
Linked List after deleting node with value 20:
5 -> 10 -> 30 -> None
Node with value 50 not found.
8. Write a program to perform depth-first search (DFS) on a
graph.
class Graph:
def __init__(self):
[Link] = {} # Dictionary to store graph as an adjacency list
def add_edge(self, u, v):
"""Add an edge from vertex u to vertex v"""
if u not in [Link]:
[Link][u] = []
if v not in [Link]:
[Link][v] = []
[Link][u].append(v)
[Link][v].append(u) # For undirected graph, add edge in both
directions
def dfs(self, start):
"""Perform DFS starting from vertex 'start'"""
visited = set() # Set to track visited vertices
self._dfs_helper(start, visited)
def _dfs_helper(self, vertex, visited):
"""Helper function to perform DFS using recursion"""
[Link](vertex)
print(vertex, end=" ")
for neighbor in [Link][vertex]:
if neighbor not in visited:
self._dfs_helper(neighbor, visited)
# Example usage:
graph = Graph()
# Adding edges to the graph
graph.add_edge('A', 'B')
graph.add_edge('A', 'C')
graph.add_edge('B', 'D')
graph.add_edge('B', 'E')
graph.add_edge('C', 'F')
print("DFS traversal starting from vertex A:")
[Link]('A')
O/p:
DFS traversal starting from vertex A:
ABDECF
9. Write a program to perform breadth-first search (BFS) on a
graph.
from collections import deque
class Graph:
def __init__(self):
[Link] = {} # Dictionary to store graph as an adjacency list
def add_edge(self, u, v):
"""Add an edge from vertex u to vertex v"""
if u not in [Link]:
[Link][u] = []
if v not in [Link]:
[Link][v] = []
[Link][u].append(v)
[Link][v].append(u) # For undirected graph, add edge in both
directions
def bfs(self, start):
"""Perform BFS starting from vertex 'start'"""
visited = set() # Set to track visited vertices
queue = deque([start]) # Queue to manage the BFS order
[Link](start)
while queue:
vertex = [Link]()
print(vertex, end=" ")
# Visit all unvisited neighbors of the current vertex
for neighbor in [Link][vertex]:
if neighbor not in visited:
[Link](neighbor)
[Link](neighbor)
# Example usage:
graph = Graph()
# Adding edges to the graph
graph.add_edge('A', 'B')
graph.add_edge('A', 'C')
graph.add_edge('B', 'D')
graph.add_edge('B', 'E')
graph.add_edge('C', 'F')
print("BFS traversal starting from vertex A:")
[Link]('A')
O/p:
BFS traversal starting from vertex A:
ABCDEF
10. Write a program to determine if a binary tree is balanced.
class Node:
def __init__(self, data):
[Link] = data # Node value
[Link] = None # Left child
[Link] = None # Right child
class BinaryTree:
def __init__(self):
[Link] = None # Initially, the tree is empty
def is_balanced(self):
"""Return True if the tree is balanced, False otherwise."""
def check_balance(root):
# If the node is None, return height as 0
if root is None:
return 0
# Recursively find the height of the left subtree
left_height = check_balance([Link])
if left_height == -1:
return -1 # Left subtree is not balanced
# Recursively find the height of the right subtree
right_height = check_balance([Link])
if right_height == -1:
return -1 # Right subtree is not balanced
# If the current node is unbalanced, return -1
if abs(left_height - right_height) > 1:
return -1
# Return the height of the current node
return max(left_height, right_height) + 1
# Start checking balance from the root
return check_balance([Link]) != -1
def insert(self, data):
"""Insert a node with 'data' into the binary tree."""
if [Link] is None:
[Link] = Node(data)
else:
self._insert_recursive([Link], data)
def _insert_recursive(self, node, data):
"""Helper method to insert a node recursively."""
if data < [Link]:
if [Link] is None:
[Link] = Node(data)
else:
self._insert_recursive([Link], data)
else:
if [Link] is None:
[Link] = Node(data)
else:
self._insert_recursive([Link], data)
# Example usage:
bt = BinaryTree()
# Inserting nodes into the binary tree
[Link](10)
[Link](5)
[Link](15)
[Link](3)
[Link](7)
[Link](12)
[Link](18)
print("Is the binary tree balanced?", bt.is_balanced())
# Insert a node that will unbalance the tree
[Link](2)
print("Is the binary tree balanced after adding a new node?",
bt.is_balanced())
O/p:
Is the binary tree balanced? True
Is the binary tree balanced after adding a new node? False