0% found this document useful (0 votes)
21 views58 pages

Ilovepdf Merged

Uploaded by

shaamoffl869
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views58 pages

Ilovepdf Merged

Uploaded by

shaamoffl869
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 58

RATHINAM TECHNICAL

CAMPUS
RATHINAM TECHZONE
EACHANARI, COIMBATORE-641021.

DEPARTMENT OF COMPUTER SCIENCE AND ENGINEERING

RECORD NOTE BOOK

22CS301 DATA STRUCTURES LABORATORY

NAME :

REGISTER NUMBER :

YEAR/SEMESTER :

ACADEMIC YEAR :
RATHINAM TECHNICAL CAMPUS
RATHINAM TECHZONE
EACHANARI, COIMBATORE-641021.

BONAFIDE CERTIFICATE
NAME :

ACADEMIC YEAR :

YEAR/SEMESTER :

BRANCH :

UNIVERSITY REGISTER NUMBER: ……………………………….

Certified that this is the bonafide record of work done by the above student in the

Laboratory during the year 2024-2025.

Head of the Department Staff-in-Charge

Submitted for the Practical Examination held on

Internal Examiner External Examiner


INDEX
S. No Date Experiment Name Page Marks Staff Sign
No:
INDEX
S.No Date Experiment Name Page Marks Staff Sign
No:
Register No: 23105050

EXPT NO :
DATE : Implement Binary tree

Create a Python program that implements a binary tree data structure. The program should take
dynamic input from the user to build the tree. Each input will represent a node, and you need to insert
it into its appropriate position within the binary tree structure. You don't need to implement any
specific tree traversal algorithms for this task; just focus on the insertion logic.

Example:

Sample Input: 1

5
24135

Sample Output: 1

Tree structure successfully built!

Sample Input: 2

8
42613578

Sample Output: 2

Tree structure successfully built!

Sample Input: 3

10
8 4 12 2 6 10 14 1 3 5 7 9 11 13 15

Sample Output: 3

Tree structure successfully built!

Input Format:

The first line of input indicates the number of nodes. Subsequent lines contain space-separated integer
values representing the nodes.

Output Format:

The program should print 'Tree structure successfully built!' upon successful creation of the binary
tree.
Register No: 23105050

Constraints:

Input will always be valid, and the first number represents the total number of nodes to be inserted.
Duplicate values are allowed in the tree.

Hint:

Use a class to represent a node in the binary tree. Each node should have data, left child, and right
child attributes. Implement an insert method to add new nodes, adhering to the properties of a binary
search tree.

Naming Conventions:

Follow Python naming conventions - use CamelCase for class names and lowercase_with_underscores
for variables and methods.

Solution
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None

class BinaryTree:
def __init__(self):
self.root = None

def insert(self, data):


if self.root is None:
self.root = Node(data)
else:
self._insert(self.root, data)

def _insert(self, current, data):


if data < current.data:
if current.left is None:
current.left = Node(data)
else:
self._insert(current.left, data)
elif data > current.data:
if current.right is None:
current.right = Node(data)
else:
self._insert(current.right, data)

def main():
bt = BinaryTree()
n = int(input())
nodes = list(map(int, input().split()))
for node in nodes:
bt.insert(node)
print("Tree structure successfully built!")
Register No: 23105050

if __name__ == "__main__":
main()

Analysis:

Time Complexity - O(n) Coding Conventions - Intermediate

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Intermediate Code Reusability - Beginner

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate


Register No: 23105050

EXPT NO :
DATE : Implement Binary Search tree

Imagine you are building a library management system. You need to store book records efficiently so
that searching for a specific book by its ID is fast. Implement a Binary Search Tree in Python to store
book records (represented by unique integer IDs) dynamically based on user input. Your program
should handle insertion of new book IDs and display the tree's inorder traversal, which represents the
sorted order of book IDs.

Example:

Sample Input: 1

5
10
5
15
2
7

Sample Output: 1

2 5 7 10 15

Sample Input: 2

8
50
30
70
20
40
60
80
10

Sample Output: 2

10 20 30 40 50 60 70 80

Sample Input: 3

9
8
3
10
Register No: 23105050

1
6
14
4
7
13Sample Output: 3

1 3 4 6 7 8 10 13 14

Input Format:

The first line contains an integer 'n' representing the number of book IDs. The following 'n' lines each
contain a single integer representing a book ID.

Output Format:

Print the space-separated inorder traversal of the constructed BST.

Constraints:

The input will consist of positive integers representing book IDs. Assume all book IDs are unique.

Hint:

Use a class to represent a node in the BST and implement methods for insertion and inorder traversal.

Naming Conventions:

Use descriptive variable names like 'root' for the root of the tree, 'data' for the node's value, 'left' and
'right' for child nodes.

Solution
class Node:
def __init__(self, data):
self.data=data
self.left=None
self.right=None
class BT_S:
def __init__(self):
self.root = None
def insert(self,data):
Newnode=Node(data)
if self.root is None:
self.root=Newnode
else:
self.temp=self.root
while(True):
if data<=self.temp.data:
if self.temp.left is not None:
self.temp=self.temp.left
else:
Register No: 23105050

self.temp.left=Newnode
return
elif data >= self.temp.data:
if self.temp.right is not None:
self.temp=self.temp.right
else:
self.temp.right=Newnode
return
def printInOrder(self,node):
if node is None:
return
self.printInOrder(node.left)
print(node.data,end=" ")
self.printInOrder(node.right)
def printPreOrder(self,node):
if node is None:
return
print(node.data,end=" ")
self.printPreOrder(node.left)
self.printPreOrder(node.right)
def printPostOrder(self,node):
if node is None:
return
self.printPostOrder(node.left)
self.printPostOrder(node.right)
print(node.data,end=" ")
bst=BT_S()
size=int(input())
for i in range(size):
data = int(input())
bst.insert(data)
bst.printInOrder(bst.root)

Analysis:

Time Complexity - O(n) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%


Register No: 23105050

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : List implementation of Double


DATE :
ended Queue ADTs

Implement a double-ended queue (deque) data structure in Python using a list as the underlying
storage mechanism. Your implementation should handle dynamic input, meaning users can add or
remove elements from both ends of the queue. The program should support the following operations:
1. `append(value)`: Adds an element to the rear of the deque. 2. `appendleft(value)`: Adds an element
to the front of the deque. 3. `pop()`: Removes and returns the element at the rear of the deque. Raises
an exception if the deque is empty. 4. `popleft()`: Removes and returns the element at the front of the
deque. Raises an exception if the deque is empty. 5. `peek()`: Returns the element at the rear of the
deque without removing it. Raises an exception if the deque is empty. 6. `peekleft()`: Returns the
element at the front of the deque without removing it. Raises an exception if the deque is empty. 7.
`is_empty()`: Returns `True` if the deque is empty, `False` otherwise. 8. `size()`: Returns the number
of elements in the deque.

`0Example:

Sample Input: 1

5
append 1
append 2
appendleft 0
peekleft
pop

Sample Output: 1

0
2

Sample Input: 2

4
append 10
appendleft 20
pop
size
Sample Output: 2

10
1

Sample Input: 3
Register No: 23105050

3
appendleft 30
peekleft
size
Sample Output: 3

30

Input Format:

The first line of input indicates the number of operations to be performed. Subsequent lines each
contain a single operation followed by its input (if any), separated by a space.

Output Format:

For each 'peek', 'pop', 'popleft', or 'size' operation, output the result on a new line. If an operation
results in an error, output the error message.

Constraints:

The maximum size of the deque is limited by the available memory.

Hint:

Utilize the list methods `append()`, `insert()`, `pop()`, and `pop(0)` to efficiently implement the deque
operations. Remember to handle edge cases like popping from an empty deque.

Naming Conventions:

Use descriptive variable names like 'deque', 'value', etc.

Solution
class Queue:
def __init__(self):
self.arr=[]
def enrear(self,val):
return self.arr.append(val)
def derear(self):
return self.arr.pop()
def enfront(self,val):
return self.arr.insert(0,val)
def defront(self):
return self.arr.pop(0)
def rearpeek(self):
return self.arr[-1]
def frontpeek(self):
return self.arr[0]
def display(self):
print(len(self.arr))
Register No: 23105050

res=Queue()
n=int(input())
for i in range(n):
oper=input().split()
if len(oper)>=2:
operation=oper[0]
val=int(oper[1])
else:
operation=oper[0]

if operation=='append':
r= res.enrear(val)
elif operation=='appendleft':
res.enfront(val)
elif operation=='pop':
r=res.derear()
print(r)
elif operation=='popleft':
r=res.defront()
print(r)
elif operation=='peek':
r=res.rearpeek()
print(r)
elif operation=='peekleft':
r=res.frontpeek()
print(r)
elif operation=='size':
res.display()

Analysis:

Time Complexity - O(1) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : Implement Graph using


DATE :
Adjacency List.

Imagine you're organizing a social network where users can be friends. Your task is to represent this
network efficiently using an adjacency list in Python. Each person will be a node in our graph, and a
friendship between two people will be represented by an edge. An adjacency list is a way to store this
information by using a dictionary: the keys are the nodes, and the value associated with each key is a
list of nodes directly connected to it (its neighbors). Your goal is to write a Python program that allows
you to add users (nodes) and friendships (edges) to this social network graph. Since it's an undirected
graph, if User A is friends with User B, then User B is also friends with User A.

Example:

Sample Input: 1

3
AddNode 0
AddNode 1
AddEdge 0 1

Sample Output: 1

{0: [1], 1: [0]}

Sample Input: 2

5
AddNode 0
AddNode 1
AddEdge 0 1
AddNode 2
AddEdge 1 2

Sample Output: 2

{0: [1], 1: [0, 2], 2: [1]}

Sample Input: 3

8
AddNode 0
AddNode 1
AddNode 2
AddEdge 0 1
Register No: 23105050

AddEdge 1 2
AddNode 3
AddEdge 0 3
AddEdge 1 3

Sample Output: 3

{0: [1, 3], 1: [0, 2, 3], 2: [1], 3: [0, 1]}

Input Format:

The input will consist of multiple lines. The first line contains an integer 'N' representing the number
of operations. The following 'N' lines will each contain an operation, either 'AddNode x' (where x is
the node value) or 'AddEdge x y' (where x and y are the nodes to connect).

Output Format:

The output should be a string representation of the adjacency list, with nodes as keys and their
corresponding neighbor lists as values, enclosed in curly braces '{}'.

Constraints:

Assume input nodes are non-negative integers. 'AddEdge' should handle the undirected nature of the
graph.

Hint:

Use a dictionary in Python where keys represent nodes and values are lists of adjacent nodes.
Implement 'AddNode' to add keys and 'AddEdge' to update adjacency lists.

Naming Conventions:

Use descriptive variable names like 'graph', 'node', 'neighbor' for better code readability.

Solution
graph={}
def AddNode(node):
if node not in graph:
graph[node]=[]
def AddEdge(node1,node2):
AddNode(node1)
AddNode(node2)
graph[node1].append(node2)
graph[node2].append(node1)

for i in range(int(input())):
op=input().split()

if op[0]=="AddNode":
AddNode(int(op[1]))
Register No: 23105050

else:
AddEdge(int(op[1]), int(op[2]))
print(graph)

Analysis:

Time Complexity - O(N) Coding Conventions - Beginner

Space Complexity - O(N) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : Implement Graph Traversal


DATE :
Technique (BFS).

You are tasked with implementing a Python program that performs Breadth-First Search (BFS) on a
graph. Your program should take a graph represented as an adjacency list and a starting node as input.
The goal is to traverse the graph level by level from the starting node, visiting all reachable nodes.

Example:

Sample Input: 1

4
12
13
24
1

Sample Output: 1

1234

Sample Input: 2

7
12
13
24
25
36
37
1

Sample Output: 2

1234567

Sample Input: 3

10
12
13
24
35
36
Register No: 23105050

47
58
69
7 10
1

Sample Output: 3

1 2 3 4 5 6 7 8 9 10

Input Format:

The input consists of multiple lines. The first line contains an integer 'V' representing the number of
vertices in the graph. The subsequent 'V' lines each contain two space-separated integers 'u' and 'v',
indicating an edge between nodes 'u' and 'v'. The last line contains an integer representing the starting
node.

Output Format:

Print the nodes in the order they are visited during the BFS traversal, separated by spaces.

Constraints:

The graph will have a maximum of 100 nodes.

Hint:

Use a queue to store nodes at each level and mark visited nodes to avoid cycles.

Naming Conventions:

Variable names should be descriptive (e.g., 'graph', 'start_node', 'queue').

Solution
from collections import deque

def breadth_first_search(graph, src):


visited=[]
queue=deque([start_node])
visited_set=set([start_node])

while queue:
current_node = queue.popleft()
visited.append(current_node)

for adj in graph[current_node]:


if adj not in visited_set:
queue.append(adj)
visited_set.add(adj)

return visited
Register No: 23105050

num_vertices=int(input())
graph = {i: [] for i in range(1, num_vertices+1)}

for i in range(num_vertices-1):
key, value = map(int, input().split())
graph[key].append(value)
graph[value].append(key)

start_node=int(input())

bfs_result=breadth_first_search(graph, start_node)
print(" ".join(map(str, bfs_result)))

Analysis:

Time Complexity - O(V + E) Coding Conventions - Intermediate

Space Complexity - O(V) Error Handling - Beginner

Logic Analysis - Intermediate Code Reusability - Beginner

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate


Register No: 23105050

EXPT NO :
DATE : Implement Graph Traversal Tech

Imagine you're navigating a maze. You can only move forward until you hit a dead end, then you must
backtrack and try another path. This is the essence of Depth First Search (DFS). Your challenge is to
write a Python program that performs DFS on a graph, represented using an adjacency list. Given a
starting node, your program should explore the graph as far as possible along each branch before
backtracking.

Example:

Sample Input: 1

55
01
02
13
14
24
0

Sample Output: 1

01342

Sample Input: 2

67
01
02
13
14
25
35
45
0

Sample Output: 2

013524

Sample Input: 3

76
01
02
Register No: 23105050

13
24
35
46
0

Sample Output: 3

0135246

Input Format:

The first line contains two integers V and E separated by space, representing the number of vertices
and edges in the graph. The next E lines each contain two integers u and v, representing an edge
connecting vertex u and v. The last line contains a single integer, the starting node for DFS.

Output Format:

Print the nodes in the order they are visited during the DFS traversal separated by space.

Constraints:

The graph will have at most 100 nodes (0-indexed). Input edges will be valid and non-directional.

Hint:

Use a stack (or recursion) to keep track of the nodes to visit. Mark visited nodes to avoid cycles.

Naming Conventions:

Use meaningful variable names like 'graph', 'visited', 'stack', etc.

Solution
def depth_first_search(graph, start_node):
visited=set()
stack=[start_node]

visit_order=[]

while stack:
current_node=stack.pop()
if current_node not in visited:
visited.add(current_node)
visit_order.append(current_node)

for neighbor in reversed(graph[current_node]):


if neighbor not in visited:
stack.append(neighbor)

return visit_order
Register No: 23105050

v, e = input().split()
v, e = int(v), int(e)
graph={i: [] for i in range(v)}
for i in range(e):
u, v = map(int, input().split())
graph[u].append(v)
graph[v].append(u)

start_node=int(input())
dfs_traversal= depth_first_search(graph, start_node)
print(" ".join(map(str, dfs_traversal)))

Analysis:

Time Complexity - O(V + E) Coding Conventions - Beginner

Space Complexity - O(V) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO :
DATE : Implement Dijkstra’s Algorithm.

Implement Dijkstra's algorithm to find the shortest path between two nodes in a graph. The graph will
be represented as an adjacency list, where each key is a node and its value is a list of its neighbors and
the corresponding edge weights. You need to return the shortest distance from the source node to the
destination node. If there is no path between the source and destination node, return -1.

Example:

Sample Input: 1

4412113423234314

Sample Output: 1

Sample Input: 2

5612214123325443245315

Sample Output: 2

Sample Input: 3

6712513224134335646456216

Sample Output: 3

Input Format:

The first line contains two integers, N and M, representing the number of nodes and edges,
respectively. The next M lines contain three integers each, representing an edge between two nodes
and its weight. The last line contains two integers, representing the source and destination nodes.

Output Format:

An integer representing the shortest distance between the source and destination nodes, or -1 if no path
exists.
Register No: 23105050

Constraints:

1 <= number of nodes <= 10^5, 1 <= number of edges <= 10^5, 1 <= edge weight <= 10^4

Hint:

Use a priority queue to keep track of the nodes with the shortest distances from the source node.

Naming Conventions:

Variable names should be descriptive and follow Python conventions (e.g., use snake_case).

Solution
import heapq
import sys
from collections import defaultdict

def dijkstra(graph, source, destination):


priority_queue=[]
shortest_distances={node: float('inf')
for node in graph}
shortest_distances[source]=0
heapq.heappush(priority_queue, (0, source))

while priority_queue:
current_distance, current_node=heapq.heappop(priority_queue)

if current_node==destination:
return current_distance
if current_distance>shortest_distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance=current_distance+weight

if distance<shortest_distances[neighbor]:
shortest_distances[neighbor]=distance
heapq.heappush(priority_queue, (distance, neighbor))

return -1

input_data=input().strip().split()
index=0
n=int(input_data[index]); index+=1
m=int(input_data[index]); index+=1

graph = defaultdict(list)

for i in range(m):
u=int(input_data[index]); index+=1
v=int(input_data[index]); index+=1
weight=int(input_data[index]); index+=1
graph[u].append((v, weight))
graph[v].append((u, weight))

source=int(input_data[index]); index+=1
destination=int(input_data[index])
Register No: 23105050

result=dijkstra(graph, source, destination)


print(result)

Analysis:

Time Complexity - O((V + E) log V) Coding Conventions - Beginner

Space Complexity - O(V) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

Implement Open Addressing


EXPT NO :
DATE : (Linear Probing & Quadratic
Probing)

Implement a Python program that handles hash collisions in a hash table using open addressing with
both linear probing and quadratic probing techniques. **Open Addressing** Open addressing is a
method of collision resolution in hash tables. In open addressing, all elements are stored in the hash
table itself. So at any point, the size of the table must be greater than or equal to the total number of
keys. When a new element is to be inserted, the hash table is examined, starting with the hashed-to slot
and proceeding in some probe sequence, until an unoccupied slot is found. **Linear Probing** Linear
probing is a scheme used in open addressing for resolving collisions in hash tables. In linear probing,
we linearly search for the next empty slot in the table. **Quadratic Probing** Instead of using a
constant "skip" value like linear probing, quadratic probing uses a quadratic function to calculate the
skip value based on the number of attempts made to find an empty slot.

Example:

Sample Input: 1

8
10
20
30
40
50
60
70
80

Sample Output: 1

Hash Table using Linear Probing:


40
80
10
50
20
60
30
70

Hash Table using Quadratic Probing:


40
80
10
50
20
Register No: 23105050

60
30
70

Sample Input: 2

10
23
43
12
78
54
87
98
11
32
45

Sample Output: 2

Hash Table using Linear Probing:


32
11
12
23
43
54
45
87
78
98

Hash Table using Quadratic Probing:


45
11
12
23
43
54
32
87
78
98

Sample Input: 3

12
5
15
Register No: 23105050

25
35
45
55
65
75
85
95
105
115

Sample Output: 3

Hash Table using Linear Probing:


95
25
85
15
75
5
65
55
115
45
105
35

Hash Table using Quadratic Probing:


95
25
85
15
75
5
65
55
115
45
105
35

Input Format:

The first line of input represents the number of elements to be inserted into the hash table. Subsequent
lines will contain the elements themselves.

Output Format:

Print the hash table content after inserting all the elements using both Linear Probing and Quadratic
Probing. Each element should be printed on a new line along with its index.
Register No: 23105050

Constraints:

1 <= Number of Inputs <= 10^5 0 <= Each input element <= 10^9

Hint:

For Linear Probing: h(x, i) = (h'(x) + i) % m For Quadratic Probing: h(x, i) = (h'(x) + i*i) % m where,
h'(x) is the hash function, i is the attempt number (starting from 0), and m is the size of the hash table.

Naming Conventions:

Adhere to standard Python variable naming conventions.

Solution
size=int(input(""))
elements=[]
print("")
for i in range(size):
elements.append(int(input()))
def linear_probing(hash_table,element):
index=element%size
i=1
while hash_table[index] is not None:
index=(index+i)%size
i+=1
hash_table[index]=element
def quadratic_probing(hash_table,element):
index=element%size
i=1
while hash_table[index] is not None:
index=(index+i*i)%size
i+=1
hash_table[index]=element
hash_table_linear=[None]*size
hash_table_quadratic=[None]*size
print("Hash Table using Linear Probing: ")
for element in elements:
linear_probing(hash_table_linear,element)
for i in range(size):
print(f"{hash_table_linear[i]} ")
print("\nHash Table using Quadratic Probing: ")
for element in elements:
quadratic_probing(hash_table_quadratic,element)
for i in range(size):
print(f"{hash_table_quadratic[i]} ")

Analysis:

Time Complexity - O(n) Coding Conventions - Beginner


Register No: 23105050

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : Linked List implementation of


DATE :
Stack ADTs

You need to implement a Stack Abstract Data Type (ADT) using a Linked List in Python. Your
program should handle a dynamic number of inputs. The Stack ADT should support the following
operations:

1. **Push (x):** Adds an element 'x' to the top of the stack.

2. **Pop ():** Removes and returns the element at the top of the stack. If the stack is empty, it should
indicate an error (e.g., return a specific message or raise an exception).

3. **Peek():** Returns the element at the top of the stack without removing it. If the stack is empty,
handle it appropriately.

4. **isEmpty():** Checks if the stack is empty. Returns True if empty, False otherwise.

5. **Size():** Returns the number of elements in the stack.

Example:

Sample Input: 1

4
push 10
push 20
peek
pop

Sample Output: 1

20
20

Sample Input: 2

3
push 5
pop
isEmpty

Sample Output: 2

5
True
Register No: 23105050

Sample Input: 3

3
push 15
peek
isEmpty

Sample Output: 3

15
False

Input Format:

The first line of input contains an integer 'n' indicating the number of operations. The following 'n'
lines contain operations in the format: 'operationName value' (for push) or 'operationName' (for other
operations).

Output Format:

The output should print the results of each 'peek', 'pop', 'isEmpty', and 'size' operation on separate
lines, as demonstrated in the sample input-output pairs.

Constraints:

The input will always be valid and follow the specified format. The number of operations (n) will be
within a reasonable range (1 <= n <= 1000).

Hint:

Use a Node class to represent elements in the linked list. Each node should store the data and a
reference to the next node. The stack can be represented by a head pointer, initially pointing to None.

Naming Conventions:

Use descriptive variable names like 'Node', 'data', 'next', 'head', 'push', 'pop', 'peek', etc.

Solution
class Node:
def __init__(self, data):
self.data = data
self.next = None

class Stack:
def __init__(self):
self.top = None

def push(self, x):


new_node = Node(x)
new_node.next = self.top
Register No: 23105050

self.top = new_node

def pop(self):
if self.isEmpty():
return "Stack is empty"
popped_value = self.top.data
self.top = self.top.next
return popped_value

def peek(self):
if self.isEmpty():
return "Stack is empty"
return self.top.data

def isEmpty(self):
return self.top is None

def size(self):
count = 0
current = self.top
while current:
count += 1
current = current.next
return count

def main():
stack = Stack()
n = int(input())
for _ in range(n):
operation = input().split()
if operation[0] == 'push':
stack.push(int(operation[1]))
elif operation[0] == 'pop':
print(stack.pop())
elif operation[0] == 'peek':
print(stack.peek())
elif operation[0] == 'isEmpty':
print(stack.isEmpty())
elif operation[0] == 'size':
print(stack.size())

if __name__ == "__main__":
main()

Analysis:

Time Complexity - O(1) Coding Conventions - Intermediate

Space Complexity - O(n) Error Handling - Beginner


Register No: 23105050

Logic Analysis - Intermediate Code Reusability - Beginner

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate


Register No: 23105050

EXPT NO : Linked List implementation of


DATE :
Queue ADTs.

Implement a Queue abstract data type (ADT) in Python using a linked list as the underlying data
structure. Your implementation should include the following methods: 1. `enqueue(data)`: Adds an
element with `data` to the rear of the queue. 2. `dequeue()`: Removes and returns the element at the
front of the queue. Raises an exception if the queue is empty. 3. `front()`: Returns the element at the
front of the queue without removing it. Raises an exception if the queue is empty. 4. `is_empty()`:
Returns `True` if the queue is empty, `False` otherwise. 5. `size()`: Returns the number of elements in
the queue.

Example:

Sample Input: 1

4
enqueue 10
enqueue 20
dequeue
size

Sample Output: 1

10
1

Sample Input: 2

3
enqueue 5
enqueue 15
dequeue

Sample Output: 2

Sample Input: 3

3
enqueue 30
dequeue
size
Register No: 23105050

Sample Output: 3

30
0

Input Format:

The first line of input contains an integer 'n' representing the number of operations. The following 'n'
lines each contain an operation to be performed on the queue. Each operation is specified by a
keyword followed by optional arguments (e.g., 'enqueue 5').

Output Format:

For each 'dequeue' and 'front' operation, print the returned value on a new line. For each 'size'
operation, print the size of the queue on a new line.

Constraints:

The input will always be valid and follow the specified format. The maximum size of the queue is not
specified.

Hint:

Use a class to represent the queue and a separate class to represent the nodes in the linked list. Each
node should store the data and a reference to the next node.

Naming Conventions:

Use descriptive names like 'Queue' for the queue class, 'Node' for the node class, 'data' for the data
stored in a node, 'front' for the front of the queue, 'rear' for the rear of the queue, etc.

Solution
class Node:
def __init__(self, data):
self.data = data
self.next = None

class Queue:
def __init__(self):
self.front = None
self.rear = None
self._size = 0

def enqueue(self, data):


new_node = Node(data)
if self.is_empty():
self.front = self.rear = new_node
else:
self.rear.next = new_node
self.rear = new_node
self._size += 1
Register No: 23105050

def dequeue(self):
if self.is_empty():
return "Queue is empty"
dequeued_value = self.front.data
self.front = self.front.next
if self.front is None:
self.rear = None
self._size -= 1
return dequeued_value

def front_value(self):
if self.is_empty():
return "Queue is empty"
return self.front.data

def is_empty(self):
return self.front is None

def size(self):
return self._size

def main():
queue = Queue()
n = int(input())
for _ in range(n):
operation = input().split()
if operation[0] == "enqueue":
queue.enqueue(int(operation[1]))
elif operation[0] == "dequeue":
print(queue.dequeue())
elif operation[0] == "front":
print(queue.front_value())
elif operation[0] == "is_empty":
print(queue.is_empty())
elif operation[0] == "size":
print(queue.size())

if __name__ == "__main__":
main()

Analysis:

Time Complexity - O(1) Coding Conventions - Intermediate

Space Complexity - O(n) Error Handling - Intermediate

Logic Analysis - Intermediate Code Reusability - Intermediate


Register No: 23105050

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate


Register No: 23105050

EXPT NO : List implementation of Stack


DATE :
ADTs

Implement a Stack Abstract Data Type (ADT) in Python using a list as the underlying data structure.
Your implementation should handle a dynamic number of inputs, allowing the user to perform stack
operations like push, pop, peek, and check if the stack is empty.

Example:

Sample Input: 1

5
push 1
push 2
peek
pop
peek

Sample Output: 1

2
1

Sample Input: 2

7
push 10
push 20
push 30
pop
peek
push 40
peek

Sample Output: 2

20
40

Sample Input: 3

9
push 100
push 200
Register No: 23105050

push 300
pop
pop
peek
push 400
pop
peek

Sample Output: 3

100
100

Input Format:

The first line contains an integer 'n' representing the number of operations. The following 'n' lines each
contain an operation in the format: 'operation' or 'operation value', where 'operation' can be 'push',
'pop', 'peek', or 'isEmpty'.

Output Format:

For each 'peek' operation, print the top element of the stack on a new line. For 'pop' operations, output
nothing if the stack is empty.

Edge Case scenario:


if the stack correctly handles multiple pop operations and returns True when it's empty.(captured in
hidden test case)

Constraints:

The input will always be valid and follow the specified format. The number of inputs (operations) will
be a positive integer.

Hint:

Use a Python list to store stack elements. Implement methods for push (append to the list), pop
(remove from the end of the list), peek (access the last element), and check if the list is empty.

Naming Conventions:

Use descriptive variable names like 'stack', 'element', 'operation', etc.

Solution
class Stack:
def __init__(self):
self.stack=[]
def push(self,data):
Register No: 23105050

self.stack.append(data)
def pop(self):
if not self.is_empty():
return self.stack.pop()

def peek(self):
if not self.is_empty():
return self.stack[-1]

def is_empty(self):
return len(self.stack)==0

if __name__ == "__main__":
s=Stack()
n=int(input())
for i in range(n):
temp=input()
operation=temp.split()
if operation[0]=='push':
data=int(operation[1])
s.push(data)

elif operation[0]=='pop':
s.pop()
elif operation[0]=='peek':
print(s.peek())
elif operation[0]=='isEmpty':
print(s.is_empty())

Analysis:

Time Complexity - O(1) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : List implementation of Circular


DATE :
Queue ADTs

Imagine you're designing a system to manage song requests in a music streaming app. Users can add
songs to a queue, and the system plays them in the order they were added. However, to avoid
extremely long wait times, you've decided to limit the queue's size. Your task is to implement this song
request queue using a circular queue data structure in Python. Since the number of song requests can
vary greatly, your implementation should dynamically adjust the queue's size as needed.

Example:

Sample Input: 1

5
ENQUEUE Faded
ENQUEUE Closer
ENQUEUE Let Her Go
DEQUEUE
ENQUEUE One Last Time

Sample Output: 1

Faded added to the queue


Closer added to the queue
Let Her Go added to the queue
Faded is removed from the queue
One Last Time added to the queue

Sample Input: 2

7
ENQUEUE Shape of You
ENQUEUE Despacito
ENQUEUE Believer
DEQUEUE
DEQUEUE
ENQUEUE Thunder
ENQUEUE Happy

Sample Output: 2

Shape of You added to the queue


Despacito added to the queue
Believer added to the queue
Shape of You is removed from the queue
Despacito is removed from the queue
Register No: 23105050

Thunder added to the queue


Happy added to the queue

Sample Input: 3

4
ENQUEUE Song A
ENQUEUE Song B
DEQUEUE
DEQUEUE

Sample Output: 3

Song A added to the queue


Song B added to the queue
Song A is removed from the queue
Song B is removed from the queue

Input Format:

The first line of input indicates the number of operations. Subsequent lines each contain an operation:
'ENQUEUE ' to add a song or 'DEQUEUE' to remove a song.

Output Format:

For each operation, print a message indicating the result: 'Queue is Empty' if DEQUEUE is called on
an empty queue, ' added to the queue' after adding a song, or ' is removed from the queue' after
removing a song.

Constraints:

The maximum number of songs in the queue at any given time will not exceed 1000.

Hint:

Use a fixed-size list and manage its elements in a circular fashion using modulo operation for index
calculation. Use front and rear pointers to keep track of the start and end of the queue.

Naming Conventions:

Use descriptive variable names like 'queue', 'front', 'rear', 'size' etc.

Solution
class Node:
def __init__(self,data):
self.data=data
self.next=None
Register No: 23105050

def str(self):
return self.data

class Queue:
def __init__(self):
self.rear=None
self.front=None

def enqueue(self,data):
newnode=Node(data)
if self.rear is None:
self.rear=newnode
self.front=newnode
self.rear.next=self.front
else:
self.rear.next=newnode
self.rear=newnode
self.rear.next=self.front
print(f"{newnode.data} added to the queue")

def dequeue(self):
if self.front is None:
print("Queue is empty")
return

removed=self.front.data
if self.front==self.rear:
self.front=None
self.rear=None
else:
self.front=self.front.next
self.rear.next=self.front

print(f"{removed} is removed from the queue")

obj=Queue()
n=int(input())
for i in range(n):
op=input().split()
oper=op[0]

if oper=='ENQUEUE':
st=' '.join(op[1:])
obj.enqueue(st)
elif oper=='DEQUEUE':
obj.dequeue()

Analysis:
Register No: 23105050

Time Complexity - O(1) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : Singly linked list implementation


DATE :
using python

Your task is to implement a Singly Linked List in Python. A Singly Linked List is a linear data
structure where each element (node) points to the next element in the sequence. The last element's next
pointer points to null, indicating the end of the list. Your implementation should include the following
operations:

1. Insertion:
Insert at the beginning of the list
Insert at the end of the list
Insert after a given node
2. Deletion:
Delete the head node
Delete the tail node
Delete a node with a given value
3. Traversal:
Print all the elements of the linked list

Example:

Sample Input: 1

8
1 10
1 20
2 30
3 25 20
4
6 30
5
4

Sample Output: 1

20 25 10 30
20 25

Sample Input: 2

10
1 15
1 10
2 20
2 25
3 20 30
Register No: 23105050

4
5
4
6 10
4

Sample Output: 2

10 15 20 25
10 15 20
15 20

Sample Input: 3

6
15
2 10
4
5
4
65

Sample Output: 3

5 10
5

Input Format:
1. The first line contains an integer n (number of operations).
2. The next n lines contain the following operations:
1 x: Insert x at the beginning.
2 x: Insert x at the end.
3 x y: Insert x after the node with value y.
4: Print the current linked list.
5: Delete the tail node.
6 x: Delete the node with value x.

Output Format:

For every 4 operation, print the linked list elements in one line, separated by a space. If the list is
empty, print "The list is empty."

Constraints:

1. The number of operations (insertions, deletions, printing) will be within the range [1, 1000]. 2. The
values to be inserted will be integers within the range [-1000, 1000].

Hint:
Register No: 23105050

Use a class to represent the Node of the linked list, containing data and a reference to the next node.
Another class can represent the LinkedList itself, containing the head of the list and methods for
various operations.

Naming Conventions:

Use CamelCase for class names (e.g., `LinkedList`, `Node`) and lowercase with underscores for
variable and function names (e.g., `head_node`, `insert_at_end`).

Solution
class Node:
def __init__(self,data):
self.data=data
self.next=None

class Sll:
def __init__(self):
self.head=None
self.temp=None

def creation(self,data):
newnode=Node(data)
if self.head is None:
self.head=newnode
self.temp=newnode

else:
self.temp.next=newnode
self.temp=newnode

def insert_at_beginning(self,data):
newnode=Node(data)
newnode.next=self.head
self.head=newnode

def insert_at_end(self,data):
newnode=Node(data)
self.temp=self.head
while(self.temp.next!=None):
self.temp=self.temp.next
self.temp.next=newnode

def insert_after(self,data,value):
if(self.head.data==value):
newnode=Node(data)
newnode.next=self.head.next
self.head.next=newnode

else:
flag=0
self.temp=self.head
while(self.temp.data!=value):
prev=self.temp
self.temp=self.temp.next
if self.temp.data!=value:
return
else:
Register No: 23105050

flag=1

if flag==1:
newnode=Node(data)
newnode.next=self.temp.next
self.temp.next=newnode

def delete_tail(self):
self.temp=self.head
while(self.temp.next!=None):
prev=self.temp
self.temp=self.temp.next
prev.next=None
del(self.temp)

def delete_node(self,value):
if(self.head.data==value):
self.temp=self.head
self.head=self.head.next
self.temp.next=None
del(self.temp)

else:
self.temp=self.head
while(self.temp.data!=value):
prev =self.temp
self.temp=self.temp.next
prev.next=self.temp.next
del(self.temp)

def display(self):
self.temp=self.head
while(self.temp):
print(self.temp.data,end=" ")
self.temp=self.temp.next
print()
if __name__== "__main__":
s=Sll()
n=int(input())
for i in range(n):
operation=input()
temp=operation.split()
num1=int(temp[0])

if num1==1:
data=int(temp[1])
s.insert_at_beginning(data)
elif num1==2:
data=int(temp[1])
s.insert_at_end(data)

elif num1==3:
data=int(temp[1])
value=int(temp[2])
s.insert_after(data,value)

elif num1==5:
s.delete_tail()

elif num1==6:
num2=int(temp[1])
Register No: 23105050

s.delete_node(num2)
elif num1==4:
s.display()

Analysis:

Time Complexity - O(n) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Intermediate Code Reusability - Intermediate

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate


Register No: 23105050

Linked List implementation of


EXPT NO :
DATE : Infix to Postfix Conversion using
Stack

Create a Python program that implements the infix-to-postfix conversion algorithm using a linked list
for the postfix expression and a stack data structure. The program should accept dynamic input,
allowing the user to enter infix expressions of varying lengths and complexities.

Example:

Sample Input: 1

a+b*c-d/e

Sample Output: 1

abc*+de/-

Sample Input: 2

(a+b)*(c-d)/e

Sample Output: 2

ab+cd-*e/

Sample Input: 3

a+b*(c^d-e)^(f+g*h)-i

Sample Output: 3

abcd^e-fgh*+^+i-

Input Format:

The input will be a single line string representing the infix expression.

Output Format:

The output will be a single line string representing the postfix expression.
Register No: 23105050

Constraints:

The input infix expressions will contain only single-letter variables, operators (+, -, *, /, ^), and
parentheses ((, )).

Hint:

Use a stack to store operators and parentheses. Process the infix expression character by character,
appending operands directly to the postfix expression and handling operators based on precedence.

Naming Conventions:

Follow standard Python naming conventions: use lowercase with underscores for variable and
function names (e.g., `infix_expression`, `push_to_stack`).

Solution
class Node:
def __init__(self, data):
self.data = data
self.next = None

class Stack:
def __init__(self):
self.top = None

def is_empty(self):
return self.top is None

def push(self, data):


new_node = Node(data)
new_node.next = self.top
self.top = new_node

def pop(self):
if self.is_empty():
return None
popped_data = self.top.data
self.top = self.top.next
return popped_data

def peek(self):
if self.is_empty():
return None
return self.top.data

def is_operand(ch):
return ch.isalnum()

def precedence(op):
if op in ('+', '-'):
return 1
if op in ('*', '/'):
return 2
if op == '^':
return 3
return -1
Register No: 23105050

def infix_to_postfix(expression):
stack = Stack()
result = []

for ch in expression:
if is_operand(ch):
result.append(ch)
elif ch == '(':
stack.push(ch)
elif ch == ')':
while not stack.is_empty() and stack.peek() != '(':
result.append(stack.pop())
stack.pop() # pop '(' from the stack
else:
while (not stack.is_empty() and
precedence(ch) <= precedence(stack.peek())):
result.append(stack.pop())
stack.push(ch)

while not stack.is_empty():


result.append(stack.pop())

return ''.join(result)

def main():
expression = input()
print(infix_to_postfix(expression))

if __name__ == "__main__":
main()

Analysis:

Time Complexity - O(n) Coding Conventions - Beginner

Space Complexity - O(n) Error Handling - Beginner

Logic Analysis - Beginner Code Reusability - Beginner

Algorithmic Analysis - Beginner Code Accuracy - 100%

Code Proficiency - Beginner


Register No: 23105050

EXPT NO : Linked List implementation of


DATE :
Stack ADTs

You need to implement a Stack Abstract Data Type (ADT) using a Linked List in Python. Your
program should handle a dynamic number of inputs. The Stack ADT should support the following
operations:

1. **Push (x):** Adds an element 'x' to the top of the stack.

2. **Pop ():** Removes and returns the element at the top of the stack. If the stack is empty, it should
indicate an error (e.g., return a specific message or raise an exception).

3. **Peek():** Returns the element at the top of the stack without removing it. If the stack is empty,
handle it appropriately.

4. **isEmpty():** Checks if the stack is empty. Returns True if empty, False otherwise.

5. **Size():** Returns the number of elements in the stack.

Example:

Sample Input: 1

4
push 10
push 20
peek
pop

Sample Output: 1

20
20

Sample Input: 2

3
push 5
pop
isEmpty

Sample Output: 2

5
True
Register No: 23105050

Sample Input: 3

3
push 15
peek
isEmpty

Sample Output: 3

15
False

Input Format:

The first line of input contains an integer 'n' indicating the number of operations. The following 'n'
lines contain operations in the format: 'operationName value' (for push) or 'operationName' (for other
operations).

Output Format:

The output should print the results of each 'peek', 'pop', 'isEmpty', and 'size' operation on separate
lines, as demonstrated in the sample input-output pairs.

Constraints:

The input will always be valid and follow the specified format. The number of operations (n) will be
within a reasonable range (1 <= n <= 1000).

Hint:

Use a Node class to represent elements in the linked list. Each node should store the data and a
reference to the next node. The stack can be represented by a head pointer, initially pointing to None.

Naming Conventions:

Use descriptive variable names like 'Node', 'data', 'next', 'head', 'push', 'pop', 'peek', etc.

Solution
class Node:
def __init__(self,data):
self.data=data
self.next=None
class Stack:
def __init__(self):
self.top=None
def push(self,x):
newnode=Node(x)
newnode.next=self.top
self.top=newnode
def pop(self):
Register No: 23105050

if self.isEmpty():
return " Stack is empty"
popped_value=self.top.data
self.top=self.top.next
return popped_value
def peek(self):
if self.isEmpty():
return "Stack is empty"
return self.top.data
def isEmpty(self):
return self.top is None
def size(self):
count=0
current=self.top
while current:
count+=1
current=current.next
return count

def main():
stack = Stack()
n=int(input())
for _ in range(n):
operation=input().split()
if operation[0]=='push':
stack.push(int(operation[1]))
elif operation[0]=='pop':
print(stack.pop())
elif operation[0]== 'peek':
print(stack.peek())
elif operation[0]=='isEmpty':
print(stack.isEmpty())
elif operation[0]=='size':
print(stack.size())

if __name__ == "__main__":
main()

Analysis:

Time Complexity - O(1) Coding Conventions - Intermediate

Space Complexity - O(n) Error Handling - Intermediate

Logic Analysis - Intermediate Code Reusability - Beginner


Register No: 23105050

Algorithmic Analysis - Intermediate Code Accuracy - 100%

Code Proficiency - Intermediate

You might also like