Lab Manual
Lab Manual
START
DECLARE int x = 10; // Simple integer variable
DECLARE float y = 15.5; // Simple float variable
// Classes and Structures
DEFINE class MyClass
DECLARE int a, b;
FUNCTION MyClass(int x, int y)
a = x;
b = y;
END
FUNCTION int sum()
RETURN a + b;
END
END
CALL BubbleSort(A)
PRINT A
END
3. Week 3: Selection sort, Bubble sort, Insertion sort.
START
// Selection Sort
FUNCTION SelectionSort(array A)
Prepared: By Medhanye
1
DECLARE int n = length(A);
FOR i FROM 0 TO n-1
DECLARE int minIdx = i;
FOR j FROM i+1 TO n
IF A[j] < A[minIdx]
minIdx = j;
END
END
SWAP A[i], A[minIdx]
END
END
// Insertion Sort
FUNCTION InsertionSort(array A)
DECLARE int n = length(A);
FOR i FROM 1 TO n-1
DECLARE int key = A[i];
DECLARE int j = i-1;
WHILE j >= 0 AND A[j] > key
A[j+1] = A[j]
j = j-1
END
A[j+1] = key
END
END
END
Prepared: By Medhanye
2
DECLARE int mid = (low + high) / 2;
IF A[mid] == target
RETURN mid;
ELSE IF A[mid] < target
low = mid + 1;
ELSE
high = mid - 1;
END
END
RETURN -1; // Not found
END
END
5. Week 5: Implementing Linked Lists: Singly linked list, Doubly linked list, Circular linked list.
START
// Singly Linked List
DEFINE class Node
DECLARE int data;
DECLARE Node* next;
END
FUNCTION SinglyLinkedList()
head = NULL;
END
Prepared: By Medhanye
3
DEFINE class DoublyLinkedList
DECLARE Node* head;
FUNCTION InsertAtEnd(int value)
DECLARE Node* newNode = new Node();
newNode->data = value;
newNode->next = NULL;
newNode->prev = NULL;
IF head == NULL
head = newNode;
ELSE
DECLARE Node* temp = head;
WHILE temp->next != NULL
temp = temp->next;
END
temp->next = newNode;
newNode->prev = temp;
END
END
END
Prepared: By Medhanye
4
DECLARE int MAX_SIZE = 100;
DECLARE int arr[MAX_SIZE];
DECLARE int top;
FUNCTION StackArray()
top = -1;
END
FUNCTION Pop()
IF top > -1
RETURN arr[top--];
ELSE
PRINT "Stack Underflow";
RETURN -1;
END
END
END
FUNCTION Pop()
IF top != NULL
DECLARE int value = top->data;
top = top->next;
RETURN value;
ELSE
PRINT "Stack Underflow";
RETURN -1;
Prepared: By Medhanye
5
END
END
END
END
FUNCTION QueueArray()
front = -1;
rear = -1;
END
FUNCTION Enqueue(int value)
IF rear < MAX_SIZE - 1
rear = rear + 1;
arr[rear] = value;
IF front == -1
front = 0;
END
ELSE
PRINT "Queue Overflow";
END
END
FUNCTION Dequeue()
IF front != -1
DECLARE int value = arr[front];
front = front + 1;
RETURN value;
ELSE
PRINT "Queue Underflow";
RETURN -1;
END
END
END
Prepared: By Medhanye
6
DECLARE Node* newNode = new Node();
newNode->data = value;
newNode->next = NULL;
IF rear == NULL
front = newNode;
rear = newNode;
ELSE
rear->next = newNode;
rear = newNode;
END
END
FUNCTION Dequeue()
IF front != NULL
DECLARE int value = front->data;
front = front->next;
RETURN value;
ELSE
PRINT "Queue Underflow";
RETURN -1;
END
END
END
END
Prepared: By Medhanye
7
current = Parent(current)
ExtractMax():
if IsEmpty():
print("Queue is empty!")
return None
maxValue = heap[0]
heap[0] = heap[length of heap - 1]
remove last element from heap
MaxHeapify(0)
return maxValue
MaxHeapify(i):
left = LeftChild(i)
right = RightChild(i)
largest = i
PeekMax():
if IsEmpty():
print("Queue is empty!")
return None
return heap[0]
Display():
print("Priority Queue:")
for element in heap:
print(element)
Week 9: Implementing Binary Search Trees.
Class TreeNode:
Initialize(value):
this.value = value
this.left = None
this.right = None
Class BinarySearchTree:
Initialize():
this.root = None
Insert(value):
if root is None:
Prepared: By Medhanye
8
root = TreeNode(value)
else:
InsertRec(root, value)
InsertRec(node, value):
if value < node.value:
if node.left is None:
node.left = TreeNode(value)
else:
InsertRec(node.left, value)
else:
if node.right is None:
node.right = TreeNode(value)
else:
InsertRec(node.right, value)
Search(value):
return SearchRec(root, value)
SearchRec(node, value):
if node is None:
return False
if value == node.value:
return True
if value < node.value:
return SearchRec(node.left, value)
else:
return SearchRec(node.right, value)
Delete(value):
root = DeleteRec(root, value)
DeleteRec(node, value):
if node is None:
return node
if value < node.value:
node.left = DeleteRec(node.left, value)
else if value > node.value:
node.right = DeleteRec(node.right, value)
else:
if node.left is None:
return node.right
else if node.right is None:
return node.left
temp = FindMin(node.right)
node.value = temp.value
node.right = DeleteRec(node.right, temp.value)
return node
FindMin(node):
Prepared: By Medhanye
9
current = node
while current.left is not None:
current = current.left
return current
InOrderTraversal():
InOrderRec(root)
InOrderRec(node):
if node is not None:
InOrderRec(node.left)
print(node.value)
InOrderRec(node.right)
PreOrderTraversal():
PreOrderRec(root)
PreOrderRec(node):
if node is not None:
print(node.value)
PreOrderRec(node.left)
PreOrderRec(node.right)
PostOrderTraversal():
PostOrderRec(root)
PostOrderRec(node):
if node is not None:
PostOrderRec(node.left)
PostOrderRec(node.right)
print(node.value)
Display():
InOrderTraversal()
Week 10: Graphs: Implementing Graph Traversal Algorithms.
Class Graph:
Initialize():
this.graph = {} # An adjacency list
AddEdge(u, v):
if u not in graph:
graph[u] = []
graph[u].append(v)
if v not in graph:
graph[v] = []
graph[v].append(u)
BFS(start):
visited = set() # Set of visited nodes
queue = Queue() # Queue for BFS
Prepared: By Medhanye
10
queue.enqueue(start)
visited.add(start)
while not queue.isEmpty():
node = queue.dequeue()
print(node) # Process the node (e.g., print it)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.enqueue(neighbor)
Week 11: Advanced Sorting Algorithms: Shell sort, Heap sort.
1. Shell Sort
Shell Sort is an extension of the insertion sort algorithm that allows the exchange of items that
are far apart. It works by comparing elements separated by a gap and reducing the gap
progressively until the gap becomes 1 (in which case, the algorithm becomes an insertion sort)
Function ShellSort(array):
# Start with a large gap and reduce the gap after each iteration
gap = length(array) // 2 # Initial gap size
while gap > 0:
# Perform a modified insertion sort with the given gap
for i = gap to length(array) - 1:
temp = array[i]
j=i
# Move elements that are greater than temp to one position ahead
while j >= gap and array[j - gap] > temp:
array[j] = array[j - gap]
j = j - gap
array[j] = temp
# Reduce the gap size
gap = gap // 2
Explanation:
Gap Sequence: The gap starts from half the array size and is reduced by half after each iteration.
Modified Insertion Sort: For each gap, the array is sorted as if it were made up of several
subarrays where elements are gap positions apart. This is a more efficient approach than the
standard insertion sort.
The process continues until the gap becomes 1, at which point the algorithm becomes a
standard insertion sort
2. Heap Sort
Heap Sort is a comparison-based sorting algorithm that uses a binary heap data structure. It first
builds a max-heap (or min-heap) from the input data and then repeatedly extracts the maximum
Prepared: By Medhanye
11
element from the heap, placing it at the end of the array. The heap property ensures that the
largest element is always at the root of the heap.
Function HeapSort(array):
n = length(array)
# Step 1: Build a max-heap
for i = n // 2 - 1 to 0: # Start from the last non-leaf node
MaxHeapify(array, n, i)
# Step 2: Extract elements from the heap
for i = n - 1 to 1:
# Swap the current root (maximum) with the last element
Swap(array[0], array[i])
# Call MaxHeapify on the reduced heap
MaxHeapify(array, i, 0)
Function MaxHeapify(array, n, i):
largest = i
left = 2 * i + 1 # Left child
right = 2 * i + 2 # Right child
# Find the largest element among the root, left child, and right child
if left < n and array[left] > array[largest]:
largest = left
if right < n and array[right] > array[largest]:
largest = right
# If the largest element is not the root, swap and heapify the affected subtree
if largest != i:
Swap(array[i], array[largest])
MaxHeapify(array, n, largest)
Function Swap(a, b):
temp = a
a=b
b = temp
Explanation:
1. Max-Heap Construction:
o A binary heap is a complete binary tree where each parent node is greater than its child
nodes (for a max-heap).
o We build the heap by calling the MaxHeapify function starting from the last non-leaf
node (index n//2 - 1).
2. Heapify:
o In the MaxHeapify function, we ensure the subtree rooted at a given index maintains
the max-heap property. If the largest element is not at the root, we swap the root with
the largest child and recursively call MaxHeapify on the affected subtree.
3. Sorting:
Prepared: By Medhanye
12
o After building the max-heap, we repeatedly extract the maximum element (the root of
the heap), swap it with the last element of the array, reduce the heap size, and re-
heapify the root.
1. Quick Sort
Explanation:
Pivot Selection: In this pseudocode, we select the last element of the array as the pivot. Other
pivot selection strategies (e.g., first element, random element) can also be used.
Partitioning: The Partition function rearranges the array such that elements less than the
pivot are on the left and elements greater than the pivot are on the right.
Recursion: Once partitioned, the array is recursively divided into two subarrays and sorted until
the base case is reached (when the subarray has one or zero elements)
2. Merge Sort
Prepared: By Medhanye
13
Merge Sort is a stable, divide-and-conquer sorting algorithm that divides the array into two
halves, recursively sorts each half, and then merges the sorted halves back together.
Function MergeSort(array, left, right):
n1 = middle - left + 1
n2 = right - middle
for i = 0 to n1 - 1:
leftArray[i] = array[left + i]
for j = 0 to n2 - 1:
rightArray[j] = array[middle + 1 + j]
14
j = 0 # Initial index for right subarray
array[k] = leftArray[i]
i=i+1
else:
array[k] = rightArray[j]
j=j+1
k=k+1
array[k] = leftArray[i]
i=i+1
k=k+1
array[k] = rightArray[j]
j=j+1
k=k+1
Explanation:
Prepared: By Medhanye
15
Divide: The array is recursively divided into two halves until each subarray has one or zero
elements.
Merge: The Merge function merges two sorted subarrays into a single sorted array. It compares
the smallest unprocessed elements of both subarrays and builds the sorted array by repeatedly
selecting the smaller element.
Stable Sort: Merge Sort is a stable sort, meaning that it preserves the relative order of elements
with equal values.
Week 13: Advanced Searching Algorithm: Hashing (Open and Closed Hashing).
Hashing is a technique used to efficiently search, insert, and delete data. It uses a hash function to map
data (keys) to fixed-size slots in an array (hash table). The two common types of hashing are Open
Hashing and Closed Hashing (also known as Open Addressing).
In Open Hashing, each slot of the hash table contains a linked list (or chain). Multiple elements
that hash to the same slot are stored in the linked list at that slot. This method handles collisions
by allowing multiple elements to exist at the same index.
Pseudocode for Open Hashing (Separate Chaining):
Function HashTableInsert(table, key, value):
index = HashFunction(key) # Calculate the hash index for the key
if table[index] is empty:
table[index] = new LinkedList() # Create a new linked list if empty
Insert into LinkedList(table[index], key, value)
Prepared: By Medhanye
16
Function LinkedListSearch(list, key):
current = list.head
while current is not null:
if current.key == key:
return current.value
current = current.next
return None # Key not found
Explanation:
Insert: When inserting a key-value pair, we calculate the index using the hash function. If there
are no existing entries at the calculated index, we create a new linked list. Otherwise, we insert
the key-value pair into the linked list at that index.
Search: To search for a key, we use the hash function to find the corresponding index and then
search the linked list at that index for the key.
Delete: To delete a key, we use the hash function to find the corresponding index and then
delete the key from the linked list if it exists.
In Closed Hashing, all elements are stored directly in the hash table. When a collision occurs, we
probe for the next available slot based on a predefined strategy. The common probing techniques
are linear probing, quadratic probing, and double hashing.
Linear Probing (Closed Hashing)
In linear probing, when a collision occurs, the algorithm checks the next slot (index + 1) until an
empty slot is found.
Prepared: By Medhanye
17
Pseudocode for Linear Probing (Closed Hashing):
Function LinearProbingInsert(table, key, value):
index = HashFunction(key) # Calculate the hash index for the key
while table[index] is not empty:
if table[index].key == key: # Key already exists, update value
table[index].value = value
return
index = (index + 1) % table.size # Move to the next index
table[index] = new Entry(key, value) # Insert at empty slot
Explanation:
Insert: The function starts at the hash index and looks for an empty slot. If a collision occurs (slot
is not empty), it probes the next index in a linear manner.
Search: Similar to insertion, but it returns the value associated with the key if found.
Delete: Deletes the key and marks the slot as empty.
Advantages:
Efficient use of space as all elements are stored in the hash table.
Linear probing is simple to implement.
Disadvantages:
Prepared: By Medhanye
18
Clustering: Consecutive elements may be hashed to the same location, leading to longer search
times.
Deletion can cause gaps, requiring special handling during rehashing.
Prepared: By Medhanye
19