Reference
Reference
Objective: To implement a Linked List data structure to understand the dynamic organization of data. The goal is to
perform fundamental operations such as insertion, deletion, searching, and reversing the list.
Theory: A Linked List is a linear data structure where elements, known as nodes, are not stored at contiguous
memory locations. Each node consists of two parts: the data field, which stores the actual information, and the link (or
next) field, which holds the reference to the next node in the sequence. This structure allows for efficient insertion and
deletion of elements, as the list can grow or shrink dynamically without shifting existing elements. In this specific
experiment, we simulate this behavior using an "Array of Structures," where integer indices are used instead of
pointers to connect the nodes within a static memory block.
Algorithm:
1. Initialization:
1. Set head = -1 to indicate an empty list.
2. Set free_pos = 0 to track the first available index in the array.
2. Insert (General Logic):
1. Check if free_pos >= MAX (Overflow). If yes, stop.
2. Allocate a new index n from free_pos.
3. Read data into list[n].data.
4. Update indices:
■ Begin: Set list[n].next = head, then update head = n.
■ End: Traverse to the last node (where next == -1), link it to n, and set list[n].next
= -1.
Position: Traverse to the node before the target position, link n between them by
■
adjusting indices.
3. Delete (General Logic):
1. If head == -1, print "Empty".
2. Begin: Update head to list[head].next.
3. End/Position: Traverse to the specific node using indices, then update the previous node's next
field to skip the current node (e.g., [Link] = [Link]).
4. Reverse:
1. Initialize prev = -1, curr = head.
2. Loop while curr != -1:
■ Store the next node's index: next = list[curr].next.
■ Reverse the link: list[curr].next = prev.
■ Shift pointers: prev = curr, curr = next.
3. Update head = prev.
Code:
// Exp 4A: Implementation of Linked lists: inserting, deleting, and inverting a linked list
// 15-12-2025
#include <stdio.h>
#define MAX 50
struct node {
int data;
int next;
};
int free_pos = 0;
int getnode() {
printf("Overflow\n");
return -1;
return free_pos++;
void insert_begin();
void insert_end();
void insert_pos();
void delete_begin();
void delete_end();
void delete_pos();
void search();
void display();
void reverse();
void insert_begin() {
int n = getnode();
if (n == -1) return;
printf("Data: ");
scanf("%d", &list[n].data);
list[n].next = head;
head = n;
void insert_end() {
int n = getnode();
if (n == -1) return;
printf("Data: ");
scanf("%d", &list[n].data);
list[n].next = -1;
if (head == -1) {
head = n;
} else {
temp = list[temp].next;
list[temp].next = n;
void insert_pos() {
printf("Pos: ");
scanf("%d", &pos);
if (pos == 1) {
insert_begin();
return;
temp = head;
temp = list[temp].next;
i++;
if (temp == -1) {
printf("Out of range\n");
} else {
n = getnode();
if (n == -1) return;
printf("Data: ");
scanf("%d", &list[n].data);
list[n].next = list[temp].next;
list[temp].next = n;
void delete_begin() {
if (head == -1) {
printf("Empty\n");
return;
head = list[head].next;
void delete_end() {
if (head == -1) {
printf("Empty\n");
return;
if (list[head].next == -1) {
head = -1;
} else {
prev = temp;
temp = list[temp].next;
list[prev].next = -1;
void delete_pos() {
printf("Pos: ");
scanf("%d", &pos);
if (pos == 1) {
head = list[head].next;
return;
temp = head;
prev = temp;
temp = list[temp].next;
i++;
void search() {
printf("Key: ");
scanf("%d", &key);
if (list[temp].data == key) {
return;
temp = list[temp].next;
pos++;
printf("Not found\n");
void display() {
temp = list[temp].next;
printf("NULL\n");
void reverse() {
next = list[curr].next;
list[curr].next = prev;
prev = curr;
curr = next;
head = prev;
int main() {
int choice;
while (1) {
printf("\[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]\n");
printf("Choice: ");
scanf("%d", &choice);
switch (choice) {
default: printf("Invalid\n");
Output:
Plaintext
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 1
Data: 10
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 2
Data: 20
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 2
Data: 30
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 8
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 9
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 8
[Link] Begin [Link] [Link] [Link] Begin [Link] End [Link] Pos [Link] [Link] [Link] [Link]
Choice: 10
Conclusion: In this experiment, we successfully studied the Linked List data structure. It was observed that unlike
arrays, linked lists allow for efficient memory utilization and flexible data management. The implementation using an
array of structures provided a practical understanding of how nodes are linked logically even if they are stored
physically in a static block.
Experiment: Implementation of Circular Queue using Linked List
Objective: To implement the Circular Queue data structure using a Linked List to demonstrate the First-In-First-Out
(FIFO) principle with dynamic memory allocation.
Theory: A Circular Queue implemented using a Linked List is a linear data structure where the last node is
connected back to the first node to form a circle. Unlike a linear linked list where the last node points to NULL, here
the next pointer of the rear node always points to the front node.
Unlike the Array implementation, this method does not require the modulo (%) operator to wrap around. It overcomes
the size limitation of static arrays as memory is allocated dynamically using malloc.
● Enqueue: Insert at rear. Update rear to the new node and make rear->next point to front.
● Dequeue: Delete from front. Update front to front->next and ensure rear->next points to the new
front.
Algorithm:
Code:
C
// Circular Queue (using linked list) and its operations using switch case
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 06.01.2026
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next;
};
void enqueue()
{
int x;
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL)
{
printf("FULL\n");
}
else
{
printf("ENTER ELEMENT: ");
scanf("%d", &x);
newNode->data = x;
if (front == NULL)
{
front = rear = newNode;
rear->next = front;
}
else
{
rear->next = newNode;
rear = newNode;
rear->next = front;
}
printf("INSERTED %d\n", x);
}
}
void dequeue()
{
struct Node *temp;
if (front == NULL)
{
printf("EMPTY\n");
}
else if (front == rear)
{
printf("DELETED %d\n", front->data);
free(front);
front = rear = NULL;
}
else
{
temp = front;
printf("DELETED %d\n", front->data);
front = front->next;
rear->next = front;
free(temp);
}
}
void display()
{
struct Node *temp;
if (front == NULL)
{
printf("EMPTY\n");
}
else
{
printf("QUEUE ");
temp = front;
do
{
printf("%d ", temp->data);
temp = temp->next;
} while (temp != front);
printf("\n");
}
}
int main()
{
int choice;
do
{
printf("\nMENU\n1 INSERT\t2 DELETE\t3 DISPLAY\t4 EXIT\n");
printf("CHOICE: ");
scanf("%d", &choice);
switch (choice)
{
case 1:
enqueue();
break;
case 2:
dequeue();
break;
case 3:
display();
break;
case 4:
printf("EXITTED\n");
break;
default:
printf("INVALID\n");
break;
}
} while (choice != 4);
return 0;
}
Output:
Plaintext
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 1
ENTER ELEMENT: 23
INSERTED 23
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 1
ENTER ELEMENT: 34
INSERTED 34
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 2
DELETED 23
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 3
QUEUE 34
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 4
EXITTED
Conclusion: The Circular Queue using Linked List efficiently utilizes memory by allocating nodes dynamically during
runtime. It eliminates the problem of fixed size encountered in the Array implementation. The circular nature is
maintained by pointing the rear node's next pointer to the front node, allowing continuous operations without
resetting pointers.
Objective: To implement the Linear Queue data structure using a Linked List to demonstrate the First-In-First-Out
(FIFO) principle with dynamic memory allocation.
Theory: A Linear Queue implemented using a Linked List is a linear data structure where elements are arranged
sequentially. It follows the FIFO (First In First Out) principle, where insertion takes place at the rear end and deletion
happens at the front end.
Unlike the Array implementation, the Linked List implementation allows the queue to grow dynamically according to
memory availability, eliminating the "Queue Full" problem (overflow) unless the system memory is exhausted.
Algorithm:
Code:
C
// Linear Queue ( using linked list) and its operations
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 06.01.2026
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next;
};
void enqueue() {
int x;
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
if(newNode == NULL) {
printf("FULL\n");
} else {
printf("ENTER ELEMENT: ");
scanf("%d", &x);
newNode->data = x;
newNode->next = NULL;
if(front == NULL && rear == NULL) {
front = rear = newNode;
} else {
rear->next = newNode;
rear = newNode;
}
printf("INSERTED %d\n", x);
}
}
void dequeue() {
struct Node *temp;
if(front == NULL) {
printf("EMPTY\n");
} else {
temp = front;
printf("DELETED %d\n", front->data);
if(front == rear) {
front = rear = NULL;
} else {
front = front->next;
}
free(temp);
}
}
void display() {
struct Node *temp;
if(front == NULL) {
printf("EMPTY\n");
} else {
temp = front;
printf("QUEUE ");
while(temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
}
int main() {
int choice;
do {
printf("\nMENU\n1 INSERT\t2 DELETE\t3 DISPLAY\t4 EXIT\n");
printf("CHOICE: ");
scanf("%d", &choice);
switch (choice) {
case 1: {
enqueue();
break;
}
case 2: {
dequeue();
break;
}
case 3: {
display();
break;
}
case 4: {
printf("EXITTED\n");
break;
}
default: {
printf("INVALID\n");
break;
}
}
} while (choice != 4);
return 0;
}
Output:
Plaintext
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 1
ENTER ELEMENT: 23
INSERTED 23
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 1
ENTER ELEMENT: 34
INSERTED 34
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 1
ENTER ELEMENT: 45
INSERTED 45
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 2
DELETED 23
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 3
QUEUE 34 45
MENU
1 INSERT 2 DELETE 3 DISPLAY 4 EXIT
CHOICE: 4
EXITTED
Conclusion: The Linear Queue using Linked List efficiently manages data in a FIFO manner. It overcomes the
fixed-size limitation of array-based queues by using dynamic memory allocation. However, unlike the Circular Queue,
the Linear Queue (in array implementation) suffers from memory wastage, but here in Linked List implementation,
memory is allocated and deallocated as needed, making it highly efficient.
—----------------------------------------------------------------------------------------------------------------------------
To implement the Stack data structure using a Linked List to demonstrate the Last-In-First-Out (LIFO) principle with
dynamic memory allocation.
Theory:
A Stack implemented using a Linked List is a linear data structure that follows the LIFO (Last In First Out) principle.
Unlike an array-based stack which has a fixed size, a linked list-based stack can grow and shrink dynamically during
runtime.
In this implementation, the top pointer represents the head of the linked list. All insertions (push) and deletions (pop)
happen at the top end to maintain $O(1)$ time complexity for these operations.
● Push: A new node is added before the current head, and top is updated to point to this new node.
● Pop: The node pointed to by top is removed, and top is moved to the next node.
Algorithm:
Code:
C
// Stack Implementation using Linked List
// Hriddhi Bhattacharyya. 10900324160. 2B-130
// DATE : 06.01.2026
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next;
};
void push(int n)
{
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL)
printf("Stack Overflow\n");
else {
newNode->data = n;
newNode->next = top;
top = newNode;
printf("%d pushed onto stack\n", n);
}
}
void pop()
{
if (top == NULL)
printf("Stack Underflow\n");
else {
struct Node *temp = top;
printf("%d popped from stack\n", top->data);
top = top->next;
free(temp);
}
}
void peak()
{
if (top == NULL)
printf("Stack is empty\n");
else
printf("Top element is %d\n", top->data);
}
void display()
{
if (top == NULL)
printf("Stack is empty\n");
else {
struct Node *temp = top;
printf("Stack elements ");
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
}
int main()
{
int choice, n;
printf("Stack Implementation using Linked List\n");
do
{
printf("1 Push 2 Pop 3 Peak 4 Display 5 Exit\n");
printf("Enter your choice ");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("Enter number to push ");
scanf("%d", &n);
push(n);
break;
case 2:
pop();
break;
case 3:
peak();
break;
case 4:
display();
break;
case 5:
printf("Exitted.\n");
break;
default:
printf("Invalid\n");
}
} while (choice != 5);
return 0;
}
Output:
Plaintext
Stack Implementation using Linked List
1 Push 2 Pop 3 Peak 4 Display 5 Exit
Enter your choice 1
Enter number to push 10
10 pushed onto stack
Conclusion:
The Stack data structure was successfully implemented using a Linked List. This approach solves the overflow
problem inherent in fixed-size array stacks (limited only by system memory). The operations push and pop are
efficient with $O(1)$ complexity as they are performed at the head of the list.
—----------------------------------------------------------------------------------------------------------------------------
To implement the Quick Sort algorithm to arrange a given set of integers in ascending order. The goal is to
understand the Divide and Conquer strategy and the partitioning mechanism.
Theory:
Quick Sort is a highly efficient sorting algorithm and is based on the Divide and Conquer algorithm. It picks an
element as a pivot and partitions the given array around the picked pivot.
● Partitioning: The key process in Quick Sort is partition(). The target of partitions is, given an array and an
element x of array as a pivot, put x at its correct position in a sorted array and put all smaller elements
(smaller than x) before x, and put all greater elements (greater than x) after x.
● In this implementation, the last element is chosen as the pivot.
Algorithm:
Code:
// Quick Sort
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 18.11.2025
#include <stdio.h>
int t = *a;
*a = *b;
*b = t;
i++;
swap(&arr[i], &arr[j]);
}
}
return (i + 1);
quickSort(arr, pi + 1, high);
int main()
int n, i;
scanf("%d", &n);
int arr[n];
scanf("%d", &arr[i]);
quickSort(arr, 0, n - 1);
printf("SORTED ARRAY USING QUICK SORT:\n");
printf("\n");
return 0;
Output:
Plaintext
ENTER 5 INTEGERS:
56
34
12
6 8 12 34 56
Conclusion:
Quick Sort was successfully implemented. It is observed that Quick Sort is faster in practice than other $O(n \log n)$
algorithms like Merge Sort for smaller datasets because of its efficient cache usage and in-place sorting nature
(requiring only stack space for recursion).
Theory:
Insertion Sort is a simple sorting algorithm that works similarly to the way you sort playing cards in your hands. The
array is virtually split into a sorted and an unsorted part. Values from the unsorted part are picked and placed at the
correct position in the sorted part.
Algorithm:
1. Start from the second element (index 1) to the last element (index n-1). Let this be i.
2. Store the current element: key = arr[i].
3. Set j = i - 1.
4. While j >= 0 AND arr[j] > key:
○ Shift element to the right: arr[j + 1] = arr[j].
○ Decrement j.
5. Insert the key at the correct position: arr[j + 1] = key.
6. Repeat until the array is sorted.
Code:
// Insertion Sort
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 18.11.2025
#include <stdio.h>
int i, key, j;
key = arr[i];
j = i - 1;
arr[j + 1] = arr[j];
j = j - 1;
arr[j + 1] = key;
int main()
int n, i;
scanf("%d", &n);
int arr[n];
scanf("%d", &arr[i]);
insertionSort(arr, n);
printf("\n");
return 0;
}
Output:
Plaintext
ENTER 5 INTEGERS:
12
23
34
1 6 12 23 34
Conclusion:
Insertion Sort was successfully implemented. It was observed that while this algorithm has a time complexity of
34$O(n^2)$, making it inefficient for large lists, it performs very well for small lis5ts or lists that are nearly6 sorted, due
to its low overhead.
—----------------------------------------------------------------------------------------------------------------------------
Aha! Asol khela toh ekhon shuru! (The real game starts now!) You want the main course after the starters? Besh,
besh! (Good, good!)
Here are the reports for Merge Sort, Heap Sort, and Selection Sort, formatted exactly as you like. Copy them
quickly, don't keep the file waiting!
To implement the Merge Sort algorithm to arrange a given set of integers in ascending order. The goal is to
understand the recursive application of the Divide and Conquer strategy.
Theory:
Merge Sort is a stable, divide-and-conquer sorting algorithm. It works by recursively dividing the input array into two
halves until each sub-array contains a single element (which is inherently sorted). Then, it merges these sorted
sub-arrays back together to produce the final sorted array.
Algorithm:
Code:
// Merge Sort
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 18.11.2025
#include <stdio.h>
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
i = 0;
j = 0;
k = l;
arr[k] = L[i];
i++;
else
arr[k] = R[j];
j++;
k++;
arr[k] = L[i];
i++;
k++;
arr[k] = R[j];
j++;
k++;
if (l < r)
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
int main()
int n, i;
scanf("%d", &n);
int arr[n];
{
scanf("%d", &arr[i]);
mergeSort(arr, 0, n - 1);
printf("\n");
return 0;
Output:
Plaintext
ENTER 5 INTEGERS:
78
12
34
90
122
12 34 78 90 122
Conclusion:
Merge Sort was implemented successfully. It is highly predictable with a guaranteed time complexity of $O(n \log n)$.
However, unlike Quick Sort, it requires $O(n)$ auxiliary space for the temporary arrays, which can be a drawback for
memory-constrained systems.
Experiment: Implementation of Heap Sort
Objective:
To implement the Heap Sort algorithm to arrange a given set of integers. The goal is to understand the construction
and manipulation of the Binary Heap data structure.
Theory:
Heap Sort is a comparison-based sorting technique based on the Binary Heap data structure. It involves building a
Max-Heap from the input data (where the largest element is at the root) and then repeatedly extracting the maximum
element from the heap and placing it at the end of the sorted array.
● Heapify: The process of creating a heap data structure from a binary tree.
● It is an In-Place algorithm but is Not Stable.
Algorithm:
Code:
// Heap Sort
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 29.11.2025
#include <stdio.h>
int t = *a;
*a = *b;
*b = t;
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
largest = left;
largest = right;
if (largest != i)
swap(&arr[i], &arr[largest]);
heapify(arr, n, largest);
heapify(arr, n, i);
swap(&arr[0], &arr[i]);
heapify(arr, i, 0);
}
int main()
int n, i;
scanf("%d", &n);
int arr[n];
scanf("%d", &arr[i]);
heapSort(arr, n);
printf("\n");
return 0;
Output:
Plaintext
35
99
23
1 23 35 99
Conclusion:
Heap Sort was successfully implemented. It combines the time efficiency of Merge Sort ($O(n \log n)$) with the space
efficiency of Quick Sort ($O(1)$ extra space). It is particularly useful when memory is tight and worst-case
performance guarantees are needed.
To implement the Selection Sort algorithm to arrange a set of integers. The goal is to understand the concept of
repeatedly finding the minimum element from the unsorted part and putting it at the beginning.
Theory:
Selection Sort is a simple comparison-based sorting algorithm. The list is divided into two parts: the sorted part at the
left end and the unsorted part at the right end.
● In every iteration, the minimum element (considering ascending order) from the unsorted subarray is
picked and moved to the sorted subarray.
● It performs well on small files but is inefficient for large lists.
Algorithm:
Code:
// Selection Sort
// Hriddhi Bhattacharyya_10900324160_ECE-2B-130
// DATE: 29.11.2025
#include <stdio.h>
min_idx = i;
min_idx = j;
if (min_idx != i)
temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
int main()
int n, i;
scanf("%d", &n);
int arr[n];
scanf("%d", &arr[i]);
selectionSort(arr, n);
printf("\n");
return 0;
Output:
Plaintext
ENTER 5 INTEGERS:
88
23
90
12
102
12 23 88 90 102
Conclusion:
Selection Sort was successfully implemented. While it has a high time complexity of $O(n^2)$, it has the property of
making the minimum number of swaps (at most $n-1$), which can be advantageous when the cost of writing to
memory is high.