LAB Experiments
1. Write a program to implement stack using arrays and evaluate a given postfix expression
class Stack:
def __init__(self):
[Link] = []
def is_empty(self):
return len([Link]) == 0
def push(self, item):
[Link](item)
def pop(self):
if not self.is_empty():
return [Link]()
else:
raise IndexError("Stack is empty")
def peek(self):
if not self.is_empty():
return [Link][-1]
else:
raise IndexError("Stack is empty")
def evaluate_postfix(expression):
stack = Stack()
for char in expression:
if [Link](): # If the character is an operand, push it onto the stack
[Link](int(char))
else:
# Pop two operands for the operator
right_operand = [Link]()
left_operand = [Link]()
if char == '+':
[Link](left_operand + right_operand)
elif char == '-':
[Link](left_operand - right_operand)
elif char == '*':
[Link](left_operand * right_operand)
elif char == '/':
[Link](left_operand / right_operand)
else:
raise ValueError(f"Unknown operator: {char}")
return [Link]()
# Example Usage
if __name__ == "__main__":
postfix_expression = "231*+9-" # Example postfix expression
print(f"Postfix Expression: {postfix_expression}")
result = evaluate_postfix(postfix_expression)
print(f"Result: {result}")
Output
For the given postfix expression 231*+9-, the program will output:
mathematica
CopyEdit
Postfix Expression: 231*+9-
Result: -4
Explanation
1. Parse 2, 3, and 1 as operands.
2. Compute 3 * 1 = 3 and push the result back.
3. Add 2 + 3 = 5 and push the result back.
4. Subtract 5-9=-4.
2. Write a program to implement circular queue using arrays
class CircularQueue:
def __init__(self, size):
[Link] = size
[Link] = [None] * size
[Link] = -1
[Link] = -1
def is_full(self):
return ([Link] + 1) % [Link] == [Link]
def is_empty(self):
return [Link] == -1
def enqueue(self, item):
if self.is_full():
print("Queue is full! Cannot enqueue.")
return
if [Link] == -1: # First element to enqueue
[Link] = 0
[Link] = ([Link] + 1) % [Link]
[Link][[Link]] = item
print(f"Enqueued: {item}")
def dequeue(self):
if self.is_empty():
print("Queue is empty! Cannot dequeue.")
return None
item = [Link][[Link]]
if [Link] == [Link]: # Queue becomes empty after this operation
[Link] = [Link] = -1
else:
[Link] = ([Link] + 1) % [Link]
print(f"Dequeued: {item}")
return item
def display(self):
if self.is_empty():
print("Queue is empty!")
return
print("Circular Queue elements:")
i = [Link]
while True:
print([Link][i], end=" ")
if i == [Link]:
break
i = (i + 1) % [Link]
print()
# Example Usage
if __name__ == "__main__":
cq = CircularQueue(5) # Circular Queue of size 5
[Link](10)
[Link](20)
[Link](30)
[Link](40)
[Link](50)
[Link]()
[Link]()
[Link]()
[Link]()
[Link](60)
[Link](70)
[Link]()
Output
For the above example, the output will be:
yaml
CopyEdit
Enqueued: 10
Enqueued: 20
Enqueued: 30
Enqueued: 40
Enqueued: 50
Circular Queue elements:
10 20 30 40 50
Dequeued: 10
Dequeued: 20
Circular Queue elements:
30 40 50
Enqueued: 60
Enqueued: 70
Circular Queue elements:
30 40 50 60 70
Explanation
1. The queue is initialized with size 5.
2. Elements 10, 20, 30, 40, 50 are enqueued.
3. Two elements (10 and 20) are dequeued.
4. After dequeuing, two more elements (60 and 70) are enqueued to demonstrate the circular nature of the
queue.
3. Write a program to implement double ended queue (de queue) using
arrays
class Deque:
def __init__(self, size):
[Link] = size
[Link] = [None] * size
[Link] = -1
[Link] = -1
def is_full(self):
return ([Link] + 1) % [Link] == [Link]
def is_empty(self):
return [Link] == -1
def insert_front(self, item):
if self.is_full():
print("Deque is full! Cannot insert at front.")
return
if self.is_empty(): # First element to insert
[Link] = [Link] = 0
else:
[Link] = ([Link] - 1 + [Link]) % [Link]
[Link][[Link]] = item
print(f"Inserted {item} at the front.")
def insert_rear(self, item):
if self.is_full():
print("Deque is full! Cannot insert at rear.")
return
if self.is_empty(): # First element to insert
[Link] = [Link] = 0
else:
[Link] = ([Link] + 1) % [Link]
[Link][[Link]] = item
print(f"Inserted {item} at the rear.")
def delete_front(self):
if self.is_empty():
print("Deque is empty! Cannot delete from front.")
return None
item = [Link][[Link]]
if [Link] == [Link]: # Deque becomes empty after this operation
[Link] = [Link] = -1
else:
[Link] = ([Link] + 1) % [Link]
print(f"Deleted {item} from the front.")
return item
def delete_rear(self):
if self.is_empty():
print("Deque is empty! Cannot delete from rear.")
return None
item = [Link][[Link]]
if [Link] == [Link]: # Deque becomes empty after this operation
[Link] = [Link] = -1
else:
[Link] = ([Link] - 1 + [Link]) % [Link]
print(f"Deleted {item} from the rear.")
return item
def display(self):
if self.is_empty():
print("Deque is empty!")
return
print("Deque elements:")
i = [Link]
while True:
print([Link][i], end=" ")
if i == [Link]:
break
i = (i + 1) % [Link]
print()
# Example Usage
if __name__ == "__main__":
dq = Deque(5) # Deque of size 5
dq.insert_rear(10)
dq.insert_rear(20)
dq.insert_front(5)
dq.insert_front(2)
[Link]()
dq.delete_rear()
dq.delete_front()
[Link]()
dq.insert_rear(30)
dq.insert_front(1)
[Link]()
Output
For the above example, the output will be:
yaml
CopyEdit
Inserted 10 at the rear.
Inserted 20 at the rear.
Inserted 5 at the front.
Inserted 2 at the front.
Deque elements:
2 5 10 20
Deleted 20 from the rear.
Deleted 2 from the front.
Deque elements:
5 10
Inserted 30 at the rear.
Inserted 1 at the front.
Deque elements:
1 5 10 30
Explanation
1. Insert operations are performed at both the front and rear of the deque.
2. Delete operations remove elements from the respective ends.
3. Circular indexing ensures efficient use of the array, and the program handles both overflow and
underflow conditions effectively.
4. Write programs for applications based on stacks and queues.
Application 1: Balanced Parentheses (Stack)
class Stack:
def __init__(self):
[Link] = []
def push(self, item):
[Link](item)
def pop(self):
if not self.is_empty():
return [Link]()
return None
def peek(self):
if not self.is_empty():
return [Link][-1]
return None
def is_empty(self):
return len([Link]) == 0
def is_balanced(expression):
stack = Stack()
pairs = {')': '(', ']': '[', '}': '{'}
for char in expression:
if char in '([{':
[Link](char)
elif char in ')]}':
if stack.is_empty() or [Link]() != pairs[char]:
return False
return stack.is_empty()
# Example Usage
if __name__ == "__main__":
expr1 = "{[()()]}"
expr2 = "{[(])}"
print(f"Expression: {expr1} -> Balanced: {is_balanced(expr1)}")
print(f"Expression: {expr2} -> Balanced: {is_balanced(expr2)}")
Output:
Expression: {[()()]} -> Balanced: True
Expression: {[(])} -> Balanced: False
5. Write programs to implement the following data structures and their
applications
(a) Single linked list
(b) Double linked list
(a) Single Linked List Implementation
class Node:
def __init__(self, data):
[Link] = data
[Link] = None
class SingleLinkedList:
def __init__(self):
[Link] = None
def insert_at_end(self, data):
new_node = Node(data)
if [Link] is None:
[Link] = new_node
else:
current = [Link]
while [Link]:
current = [Link]
[Link] = new_node
print(f"Inserted {data} at the end.")
def delete_by_value(self, value):
if [Link] is None:
print("List is empty. Nothing to delete.")
return
if [Link] == value:
[Link] = [Link]
print(f"Deleted {value} from the list.")
return
current = [Link]
while [Link] and [Link] != value:
current = [Link]
if [Link]:
[Link] = [Link]
print(f"Deleted {value} from the list.")
else:
print(f"{value} not found in the list.")
def display(self):
if [Link] is None:
print("List is empty.")
return
print("Single Linked List: ", end="")
current = [Link]
while current:
print([Link], end=" -> ")
current = [Link]
print("None")
# Example Usage
if __name__ == "__main__":
sll = SingleLinkedList()
sll.insert_at_end(10)
sll.insert_at_end(20)
sll.insert_at_end(30)
[Link]()
sll.delete_by_value(20)
[Link]()
Output:
rust
CopyEdit
Inserted 10 at the end.
Inserted 20 at the end.
Inserted 30 at the end.
Single Linked List: 10 -> 20 -> 30 -> None
Deleted 20 from the list.
Single Linked List: 10 -> 30 -> None
(b) Double Linked List Implementation
A double linked list has nodes with pointers to both the previous and next nodes.
Program:
python
CopyEdit
class Node:
def __init__(self, data):
[Link] = data
[Link] = None
[Link] = None
class DoubleLinkedList:
def __init__(self):
[Link] = None
def insert_at_end(self, data):
new_node = Node(data)
if [Link] is None:
[Link] = new_node
else:
current = [Link]
while [Link]:
current = [Link]
[Link] = new_node
new_node.prev = current
print(f"Inserted {data} at the end.")
def delete_by_value(self, value):
if [Link] is None:
print("List is empty. Nothing to delete.")
return
current = [Link]
while current and [Link] != value:
current = [Link]
if current is None:
print(f"{value} not found in the list.")
return
if [Link]:
[Link] = [Link]
if [Link]:
[Link] = [Link]
if current == [Link]: # If head node is being deleted
[Link] = [Link]
print(f"Deleted {value} from the list.")
def display_forward(self):
if [Link] is None:
print("List is empty.")
return
print("Double Linked List (Forward): ", end="")
current = [Link]
while current:
print([Link], end=" <-> ")
current = [Link]
print("None")
def display_backward(self):
if [Link] is None:
print("List is empty.")
return
print("Double Linked List (Backward): ", end="")
current = [Link]
while [Link]:
current = [Link]
while current:
print([Link], end=" <-> ")
current = [Link]
print("None")
# Example Usage
if __name__ == "__main__":
dll = DoubleLinkedList()
dll.insert_at_end(10)
dll.insert_at_end(20)
dll.insert_at_end(30)
dll.display_forward()
dll.display_backward()
dll.delete_by_value(20)
dll.display_forward()
dll.display_backward()
Output:
rust
CopyEdit
Inserted 10 at the end.
Inserted 20 at the end.
Inserted 30 at the end.
Double Linked List (Forward): 10 <-> 20 <-> 30 <-> None
Double Linked List (Backward): 30 <-> 20 <-> 10 <-> None
Deleted 20 from the list.
Double Linked List (Forward): 10 <-> 30 <-> None
Double Linked List (Backward): 30 <-> 10 <-> None
Applications:
1. Single Linked List:
o Dynamic memory allocation.
o Used in scheduling, like CPU task management.
2. Double Linked List:
o Navigation in both directions (e.g., browser history, undo-redo functionality).
o Used in managing memory blocks in OS.
These programs show how to implement and apply both single and double linked lists.
(b) Double Linked List Implementation
class Node:
def __init__(self, data):
[Link] = data
[Link] = None
[Link] = None
class DoubleLinkedList:
def __init__(self):
[Link] = None
def insert_at_end(self, data):
new_node = Node(data)
if [Link] is None:
[Link] = new_node
else:
current = [Link]
while [Link]:
current = [Link]
[Link] = new_node
new_node.prev = current
print(f"Inserted {data} at the end.")
def delete_by_value(self, value):
if [Link] is None:
print("List is empty. Nothing to delete.")
return
current = [Link]
while current and [Link] != value:
current = [Link]
if current is None:
print(f"{value} not found in the list.")
return
if [Link]:
[Link] = [Link]
if [Link]:
[Link] = [Link]
if current == [Link]: # If head node is being deleted
[Link] = [Link]
print(f"Deleted {value} from the list.")
def display_forward(self):
if [Link] is None:
print("List is empty.")
return
print("Double Linked List (Forward): ", end="")
current = [Link]
while current:
print([Link], end=" <-> ")
current = [Link]
print("None")
def display_backward(self):
if [Link] is None:
print("List is empty.")
return
print("Double Linked List (Backward): ", end="")
current = [Link]
while [Link]:
current = [Link]
while current:
print([Link], end=" <-> ")
current = [Link]
print("None")
# Example Usage
if __name__ == "__main__":
dll = DoubleLinkedList()
dll.insert_at_end(10)
dll.insert_at_end(20)
dll.insert_at_end(30)
dll.display_forward()
dll.display_backward()
dll.delete_by_value(20)
dll.display_forward()
dll.display_backward()
Output:
Inserted 10 at the end.
Inserted 20 at the end.
Inserted 30 at the end.
Double Linked List (Forward): 10 <-> 20 <-> 30 <-> None
Double Linked List (Backward): 30 <-> 20 <-> 10 <-> None
Deleted 20 from the list.
Double Linked List (Forward): 10 <-> 30 <-> None
Double Linked List (Backward): 30 <-> 10 <-> None
Applications:
1. Single Linked List:
o Dynamic memory allocation.
o Used in scheduling, like CPU task management.
2. Double Linked List:
o Navigation in both directions (e.g., browser history, undo-redo functionality).
o Used in managing memory blocks in OS.