0% found this document useful (0 votes)
12 views80 pages

DS Lab

The document is a lab manual for a Data Structures course at Annamacharya Institute of Technology & Sciences, detailing the course objectives, outcomes, and experiments. It outlines the vision and mission of the institute and department, along with program educational objectives and specific outcomes. The manual includes a list of experiments, programming assignments, and guidelines for lab conduct.
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)
12 views80 pages

DS Lab

The document is a lab manual for a Data Structures course at Annamacharya Institute of Technology & Sciences, detailing the course objectives, outcomes, and experiments. It outlines the vision and mission of the institute and department, along with program educational objectives and specific outcomes. The manual includes a list of experiments, programming assignments, and guidelines for lab conduct.
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/ 80

LAB MANUAL

DATA STRUCTURES

COURSE CODE: 23HPC0502


REGULATION: R23

ANNAMACHARYA INSTITUTE OF TECHNOLOGY


& SCIENCES

(Affiliated to J.N.T.U, Anantapur, Approved by A.I.C.T.E, New Delhi)

Utukur (P), C. K. Dinne(V & M), Kadapa Dist., AP


VISION OF THE INSTITUTE

To emerge into excellence and premier institute, transforming individuals into highly
enlightenedprofessionals enriched with innovative skills entwined with intellectual, ethical
and human values.
MISSION OF THE INSTITUTE
M1: To Impart quality technical education to enhance knowledge and skills towards
employability, higher education and research.
M2: To Promote up gradation of teaching and research skills through quality infrastructure and
resources.
M3: To Enrich and elevate the rural education seekers, endow them with ethics, innovative
thinking and leadership qualities enabling them to utilize their technical skills and
competencies for the sustainable development of the Nation and mankind.
VISION OF THE DEPARTMENT
To become a Center of Excellence for highly competent and skilled Computer Science and
SoftwareEngineers with human and ethical values, to meet the challenges in the society.
MISSION OF THE DEPARTMENT
M1: Infrastructure and Curriculum Development: To produce excellent infrastructure and
innovative curriculum to prepare skilled and competent Software engineers.
M2: Skill Development: To Organize workshops, seminars, and hands-on training sessions to
equip students with the required skills and knowledge. Also emphasize the development of soft
skills like communication, leadership, teamwork and ethics.
M3: Research, Innovation and Collaboration: To Encourage students and faculty to engage in
ground-breaking research and innovative projects in association with Industry.
PROGRAM EDUCATIONAL OBJECTIVES (PEOs):
PEO1: Prospective Career: Exhibit knowledge and skills in the various domains of Computer
Science and Engineering for prospective careers in National and Global Industries.
PEO2: Higher Education: Be strong in fundamentals of Computer Science and Engineering
for successfully pursuing higher education and research in reputed institutions.
PEO3: Product Development: Apply their knowledge and innovative ideas to design and
develop products for real time problems, as per the needs of the society, government and
industries, and emerge as entrepreneurs.
PEO4: Teamwork & Life Long Learning: Be effective team member, exhibit leadership
abilities, professional ethics, communication skills, interpersonal skills and practice lifelong
learning.
PROGRAM SPECIFIC OUTCOMES (PSOS)
PSO 1: Ability to learn the applicability of various systems software elements for solving
designproblems.
PSO 2: Ability to apply their skills in the field of Web Designing, Cloud Computing,
MachineLearning, Artificial Intelligence and Internet of Things.
PROGRAM OUTCOMES

• Engineering Knowledge

• Problem Analysis

• Design/Development of Solutions

• Conduct Investigations of Complex Problems

• Modern tool usage

• The Engineer and the Society

• Environment and its sustainability

• Ethics

• Individual and Team work

• Communication

• Project Management and finance

• Life-long learning
ANNAMACHARYA INSTITUTE OF TECHNOLOGY & SCIENCES::KADAPA
(AUTONOMOUS)
(Approved by AICTE New Delhi & Affiliated to JNTUA,
Anantapuramu)Accredited by NAAC with ‘A’ grade, Bangalore

DATA STRUCTURES LAB


(Common to CSE, IT & allied branches)

Course Objectives:

The course aims to strengthen the ability of the students to identify and apply the suitable data
structure for the given real-world problem. It enables them to gain knowledge in practical
applications of data structures.

Course Outcomes: At the end of the course, Student will be able to

CO1: Explain the role of linear data structures in organizing and accessing dataefficiently
in algorithms.

CO2: Design, implement, and apply linked lists for dynamic data storage,demonstrating
understanding of memory allocation.

CO3:Develop programs using stacks to handle recursive algorithms,manage programstates,


and solve related problems.

CO4: Apply queue-based algorithms for efficient task scheduling and distinguish between
deques and priority queues and apply them appropriately to solve data management challenges.

CO5: Recognize scenarios where hashing is advantageous, and design hash-based solutions
forspecific problems and explain the role of non linear data structures in organization.

List of Experiments:

Experiment 1: Array Manipulation

i) Write a program to reverse an array.


ii) C Programs to implement the Searching Techniques – Linear & Binary Search
iii) C Programs to implement Sorting Techniques – Bubble, Selection and Insertion Sort
Experiment 2: Linked List Implementation
i) Implement a singly linked list and perform insertion and deletion operations.
ii) Develop a program to reverse a linked list iteratively and recursively.
iii) Solve problems involving linked list traversal and manipulation.
Experiment 3: Linked List Applications

i) Create a program to detect and remove duplicates from a linked list.


ii) Implement a linked list to represent polynomials and perform addition.
iii) Implement a double-ended queue (deque) with essential operations.
Experiment 4: Double Linked List Implementation

i) Implement a doubly linked list and perform various


operations tounderstand itsproperties and applications.
ii) Implement a circular linked list and perform insertion, deletion, and traversal.
Experiment 5: Stack Operations

i) Implement a stack using arrays and linked lists.


ii) Write a program to evaluate a postfix expression using a stack.
iii) Implement a program to check for balanced parentheses using a stack.
Experiment 6: Queue Operations

i) Implement a queue using arrays and linked lists.


ii) Develop a program to simulate a simple printer queue system.
iii) Solve problems involving circular queues.

Experiment 7: Stack and Queue Applications

i) Use a stack to evaluate an infix expression and convert it to postfix.


ii) Create a program to determine whether a given string is a palindrome or not.
iii) Implement a stack or queue to perform comparison and check for symmetry.
Experiment 8: Hashing

i) Implement a hash table with collision resolution techniques.


ii) Write a program to implement a simple cache using hashing.

Experiment 9: Binary Search Tree

i) Implement a BST using Linked List.


ii) Write a program to implement a traversing of BST.

Experiment 10:Graphs

i) Implement a DFS Traversal of a graph.


ii) Write a program to implement a BFS Traversal of a graph.

Textbooks:

1. Data Structures and algorithm analysis in C, Mark Allen Weiss.


2. Fundamentals of data structures in C, Ellis Horowitz, Sartaj Sahni, Dinesh Mehta.

Reference Books:

1. Algorithms and Data Structures: The Basic Toolbox by Kurt


Mehlhornand PeterSanders
2. C Data Structures and Algorithms by Alfred V. Aho, Jeffrey D. Ullman,
andJohn E.Hopcroft
3. Problem Solving with Algorithms and Data Structures" by Brad
Millerand DavidRanum
4. Introduction to Algorithms by Thomas H. Cormen, Charles E.
Leiserson,Ronald L.Rivest, and Clifford Stein
5. Algorithms in C, Parts 1-5 (Bundle): Fundamentals, Data
Structures,Sorting,Searching, and Graph Algorithms by Robert
Sedgewick.
COURSE DESCRIPTION:

This course encompasses essential skills required for Understand the significance of linear data
structures in problem-solving and basic time/space complexity analysis.Create and manage
linked lists to efficiently organize and manipulate data, emphasizing memory
efficiency.Implement and apply stacks to manage program flow and solve problems involving
expression evaluation and backtracking.Utilize queues to model real-world scenarios, such as
process scheduling and breadth- first search algorithms and understand the versatility of deques
and prioritize data management using priority queues.Explore basic concepts of hashing and
apply it to solve problems requiring fast data retrieval and management.

COURSE OBJECTIVES

The course aims to strengthen the ability of the students to identify and apply the suitable data
structure for the given real-world problem. It enables them to gain knowledge in practical
applications of data structures.

COURSE OUTCOMES

After completion of the course, students will be able to

Explain the role of linear data structures in organizing and accessing data efficiently in
algorithms.
Design, implement, and apply linked lists for dynamic data storage,demonstrating
understanding of memory allocation.
Develop programs using stacks to handle recursive algorithms,manage program states,
and solve related problems.
Apply queue-based algorithms for efficient task scheduling and distinguish between
deques and priority queues and apply them appropriately to solve data management
challenges.
Recognize scenarios where hashing is advantageous, and design hash-based solutions
forspecific problems and explain the role of non linear data structures in organization.
PRE-REQUISITES
CO-PO MAPPING

ANNAMACHARYA INSTITUTE OF TECHNOLOGY AND SCIENCES


KADAPA
DEPARTMENT OF CSE
CO-PO MAPPING

Subject Code 23HPC0502 Year & Sem I &II

Subject Name DATA STRUCTURES

Faculty Name Regulation HMR23


COURSE OUTCOMES
COs STATEMENTS
CO1 Explain the role of linear data structures in organizing and accessing data
CO2 Design, implement, and apply linked lists for dynamic datastorage
CO3 Develop programs using stacks and Queues manage program states, and solve related
problems
CO4 Recognize scenarios where hashing is advantageous, and design hash-based solutions for
specific problems.
CO5 Explain the role of non linear data structures and organize the trees.
Modern tool usage

Life-long learning
Problem analysis

Conduct investigations of

Communication
Individualhand team
The engineer and the
Design/development of

problems
complex

Environment

sustainab

managem
Engineering

ent and
Project

finance
and its
knowledge

ilityE

c
s
t

i
solutions

society

work

P P P P P P P P P PO PO PS PS
COs PO1
O O O O O O O O O 10 1 O O
1 2 3 4 5 6 7 8 9 1 2 1 2
CO1 3 1 1 2 - - - 1 - - - - 1 1

CO2 3 2 2 2 - - - 1 - - - - 1 1

CO3 3 3 3 3 - - - 1 - 1 - - 1 1

CO4 3 3 3 3 - - - 1 1 - - - 1 1

CO5 3 2 2 2 2 - - 1 - - - - 1 1

Total 15 12 9 9 2 0 0 0 0 0 0 0 5 5
No. of Cos
mapping with 5 5 5 5 1 0 0 0 0 0 0 0 5 5
POs
Average 3.0 2.4 1.8 1.8 2.0 0.00 0.0 0.0 0.0 0.0 0.00 0.0 3.0 3.0
0 0 0 0 0 0 0 0 0 0 0 0
Round(Avera 3 2 2 2 - - - - - - - - 3 3
ge
)
DO’S & DON’TS

Do’s:

Remove your shoes or wear foot socks before you enter the lab.
Report any problems with the computer to the person in charge.
Always keep quiet. Be considerate to other lab users.
Read and understand how to carry out an activity thoroughly before coming to the
laboratory.
Shut down the computer properly.

Don’ts:

Do not bring any food or drinks in the computer room.


Do not touch any part of the computer with wet hands.
Do not damage, remove, or disconnect any label, parts, cables or equipment.
Do not install or download any software or modify or delete any system files on any
lab computers.
Do not hit the keys on the computer too hard.
CD-ROM’s and other multimedia equipment are for college work only. Do not
use them for playingmusic or other recreational activities.
If you leave the lab, do not leave your personal belonging unattended.
Experiment 1: Array Manipulation

i) Write a program to reverse an array.

AIM: To write a C program to reverse an array


Description: Array reverse or reverse a array means changing the position of each number of
the given array to its opposite position from end, i.e. if a number is at position 1 then its new
position will be Arraylength, similarly if a number is at position 2 then its new position will be
Array length – 1, and so on.To reverse an array in C, you need to swap the first element with
the last element, the second element with the second-to-last element, and so on, until you reach
the middle of the array.
Program:
#include <stdio.h>
#define MAX 100
void reverseArray(int arr[], int size) {
int temp, start = 0, end = size - 1;
while (start < end) {
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}

void printArray(int arr[], int size) {


int i;
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}

int main() {
int n,arr[MAX],i;
clrscr();
printf("Enter the number of elements in the array: ");
scanf("%d", &n);
printf("Enter the elements of the array:\n");
for (i = 0; i < n; i++)
scanf("%d", &arr[i]);

printf("Original array: ");


printArray(arr, n);
reverseArray(arr, n);
printf("Reversed array: ");
printArray(arr, n);
getch();
return 0;
}

Output:
Enter the number of elements in the array: 5
Enter the elements of the array:
12 32 21 10 9
Original array: 12 32 21 10 9
Reversed array: 9 10 21 32 12
Result: Hence,the c program for reversing the array is successfully executed.

ii) C Programs to implement the Searching Techniques – Linear & Binary Search

AIM: To write a C Programs to implement the Searching Techniques – Linear & Binary Search
Description:
A linear search is the simplest approach employed to search for an element in a data set. It
examines each element until it finds a match, starting at the beginning of the data set, until the
end. The search is finished and terminated once the target element is located.Binary search
is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly
dividing in half the portion of the list that could contain the item, until you've narrowed down
the possible locations to just one.
Program:
#include <stdio.h>
#define MAX 100
// Linear search function
int linearSearch(int arr[], int n, int x) {
int i;
for (i = 0; i < n; i++) {
if (arr[i] == x) {
return i;
}
}
return -1;
}

// Binary search function


int binarySearch(int arr[], int l, int r, int x) {
while (l <= r) {
int m = l + (r - l) / 2;

// Check if x is present at mid


if (arr[m] == x)
return m;

// If x greater, ignore left half


if (arr[m] < x)
l = m + 1;

// If x is smaller, ignore right half


else
r = m - 1;
}
return -1;
}

int main() {
int n, x,arr[MAX],i;
int linear_result,binary_result;
clrscr();
printf("Enter number of elements in the array: ");
scanf("%d", &n);
printf("Enter %d sorted elements:\n", n);
for (i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}

printf("Enter the element to search: ");


scanf("%d", &x);

// Performing linear search


linear_result = linearSearch(arr, n, x);
if (linear_result == -1) {
printf("Linear Search: Element not found.\n");
} else {
printf("Linear Search: Element found at index %d.\n", linear_result);
}

// Performing binary search


binary_result = binarySearch(arr, 0, n - 1, x);
if (binary_result == -1) {
printf("Binary Search: Element not found.\n");
} else {
printf("Binary Search: Element found at index %d.\n", binary_result);
}
getch();
return 0;
}
Output:
Enter number of elements in the array: 5
Enter 5 sorted elements:
12 34 56 78 90
Enter the element to search: 78
Linear Search: Element found at index 3.
Binary Search: Element found at index 3.
Result: Thus,the c program to implement the searching techniques-linear search&Binary
search is successfully executed.

iii) C Programs to implement Sorting Techniques – Bubble, Selection and Insertion


Sort
AIM: To write a C Programs to implement the Sorting Techniques – Bubble, Selection and
Insertion Sort
Bubble Sort
AIM: To write a C Programs to implement the Bubble Sort Technique
Description:
Bubble Sort is also known as exchange sort, which arranges values by iterating over the list
several times and in each iteration the larger value gets bubble up to the end of the list. This
algorithm uses multiple passes and, in each pass, the first and second data items are compared.
if the first data item is bigger than the second, then the two items are swapped. Next the items
in second and third position are compared and if the first one is larger than the second, then
they are swapped, otherwise no change in their order. This process continues for each
successive pair of data items until all items are sorted.
Program:
#include <stdio.h>

void bubbleSort(int arr[], int size) {


int temp,i,j;
for (i = 0; i < size - 1; i++) {
for (j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap elements if they are in the wrong order
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

int main() {
int arr[50],i,n;
clrscr();
printf("Enter the no.of elements\n");
scanf("%d",&n);
printf("Enter the list of elements\n");
for(i=0;i<n;i++)
scanf("%d",&arr[i]);
printf("Original array: ");
for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

bubbleSort(arr, n);

printf("Sorted array (Bubble Sort): ");


for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
getch();
return 0;
}
Output:
Enter the no.of elements
5
Enter the list of elements
20 28 45 12 3
Original array: 20 28 45 12 3
Sorted array(Bubble Sort): 3 12 20 28 45
Result: Thus, a C program to implement bubble sort technique is executed successfully.

Selection Sort
AIM: To write a C Programs to implement the Selection Sort Technique
Description:
In selection sort, the smallest value among the unsorted elements of the array is selected in
every pass and inserted to its appropriate position into the array. First, find the smallest element
of the array and place it on the first position. Then, find the second smallest element of the
array and place it on the second position. The process continues until we get the sorted array.
The array with n elements is sorted by using n-1 pass of selection sort algorithm.
Program:
void selectionSort(int arr[], int size) {
int minIndex, temp,i,j;
for (i = 0; i < size - 1; i++) {
minIndex = i;
for (j = i + 1; j < size; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// Swap the found minimum element with the first element
temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}

int main() {
int arr[50],i,n;
clrscr();
printf("Enter the no.of elements\n");
scanf("%d",&n);
printf("Enter the list of elements\n");
for(i=0;i<n;i++)
scanf("%d",&arr[i]);
printf("Original array: ");
for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

selectionSort(arr, n);

printf("Sorted array (Selection Sort): ");


for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
getch();
return 0;
}
Output:
Enter the no.of elements
5
Enter the list of elements
20 28 45 12 3
Original array: 20 28 45 12 3
Sorted array(Selection Sort): 3 12 20 28 45
Result:Thus, a C program to implement selection sort technique is executed successfully.

Insertion Sort
AIM: To write a C Programs to implement the Insertion Sort Technique
Description:
Insertion sort is one of the best sorting techniques. It is twice as fast as Bubble sort. In Insertion
sort the elements comparisons are as less as compared to bubble sort. In this comparison the
value until all prior elements are less than the compared values is not found. This means that
all the previous values are lesser than compared value. Insertion sort is good choice for small
values and for nearly sorted values.
Program:
#include <stdio.h>

void insertionSort(int arr[], int size) {


int i, key, j;
for (i = 1; i < size; i++) {
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}

int main() {
int arr[50],n,i;
clrscr();
printf("Enter the no.of elements\n");
scanf("%d",&n);
printf("Enter the list of elements\n");
for(i=0;i<n;i++)
scanf("%d",&arr[i]);

printf("Original array: ");


for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

insertionSort(arr, n);

printf("Sorted array (Insertion Sort): ");


for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
getch();
return 0;
}
Output:
Enter the no.of elements
5
Enter the list of elements
20 28 45 12 3
Original array: 20 28 45 12 3
Sorted array(Insertion Sort): 3 12 20 28 45
Result: Thus, a C program to implement insertion sort technique is executed successfully.

Experiment- 2
Linked list Implementation
i) C Programs to Implement a singly linked list and perform insertion and deletion
operations.
AIM: To write a C Programs to implement a singly linked list and perform insertion and
deletion
Description:
A singly linked list is a type of linked list that is unidirectional, that is, it can be traversed in
only one direction from head to the last node . Each element in a linked list is called a node. A
single node contains data and a pointer to the next node which helps in maintaining the structure
of the list. Insertion into a singly linked list has two special cases .it’s insertion a new node
before the head node and after the end node. In any other case ,new node is inserted in the
middle of the list . Deleting a node from the list in three ways: delete from the beginning ,delete
from the middle,and delete from the end
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Define the Node structure
struct Node {
int data;
struct Node* next;
};
// Function to create a new node
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// Function to insert a node at the beginning
void insertAtBeginning(struct Node** head, int data) {
struct Node* newnode = createNode(data);
newnode->next = *head;
*head = newnode;
printf("Inserted %d at beginning\n",data);
}
// Function to insert a node at the end
void insertAtEnd(struct Node** head, int data) {
struct Node* newnode = createNode(data);
struct Node* last;
if (*head == NULL) {
*head = newnode;
printf("Inserted %d at the first\n",data);
return;
}
last = *head;
while (last->next != NULL) {
last = last->next;
}
last->next = newnode;
printf("Inserted %d at the end\n",data);
}
// Function to delete the first node
void deleteFirstNode(struct Node** head) {
struct Node* temp;
if (*head == NULL) return;
temp = *head;
*head = temp->next;
free(temp);
printf("First Node deleted\n");
}
// Function to delete the last node
void deleteLastNode(struct Node** head) {
struct Node * temp;
if (*head == NULL) return;
if ((*head)->next == NULL) {
free(*head);
*head = NULL;
return;
}
temp = *head;
while (temp->next->next != NULL)
temp = temp->next;
free(temp->next);
temp->next = NULL;
printf("Last Node deleted\n");
}
// Function to print the linked list
void printList(struct Node* head) {
while (head != NULL) {
printf(" %d ", head->data);
head = head->next;
}
printf("\n");
}
int main() {
struct Node* head = NULL;
clrscr();
insertAtBeginning(&head, 7);
insertAtBeginning(&head, 1);
insertAtEnd(&head,6);
insertAtEnd(&head, 4);
insertAtEnd(&head,10);
printf("Created Linked list is: ");
printList(head);
deleteFirstNode(&head);
printf("Linked List after deleting first node: ");
printList(head);
deleteLastNode(&head);
printf("Linked List after deleting last node: ");
printList(head);
getche();
return 0;
}
Output:
Inserted 7 at beginning
Inserted 1 at beginning
Inserted 6 at the end
Inserted 4 at the end
Inserted 10 at the end
Created Linked list is: 1 7 6 4 10
First Node deleted
Linked List after deleting first node: 7 6 4 10
Last Node deleted
Linked List after deleting last node: 7 6 4
Result:
Hence, The program for implementing singly linked list and performing insertion and
deletion operations has been successfully executed.

ii) Develop a program to reverse a linked list iteratively and Recursively


AIM: To Develop a program to reverse a linked list iteratively and recursively;
Description:
Each node in the linked list contains two things, data and a pointer to the next node in the list.
In order to reverse the linked list you need to iterate or reverse the list ,and at each step,we need
to reverse the link like after the first iteration head will point to null and the next element will
point to the head.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Define the Node structure
struct Node {
int data;
struct Node* next;
};

// Function to add a new node at the end of the list


void insertAtEnd(struct Node** head, int data) {
struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
struct Node* temp = *head;
newnode->data = data;
newnode->next = NULL;
if (*head == NULL) {
*head = newnode;
return;
}
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newnode;
return;
}

// Function to print the linked list


void printList(struct Node* head) {
while (head != NULL) {
printf("%d -> ", head->data);
head = head->next;
}
printf("NULL\n");
}

// Iterative method to reverse the linked list


void reverseIteratively(struct Node** head) {
struct Node* prev = NULL;
struct Node* current = *head;
struct Node* next = NULL;
while (current != NULL) {
next = current->next; // Store next node
current->next = prev; // Reverse current node's pointer
prev = current; // Move pointers one position ahead
current = next;
}
*head = prev;
}
// Recursive method to reverse the linked list
struct Node* reverseRecursively(struct Node* head) {
struct Node * rest;
if (head == NULL || head->next == NULL) return head;
rest = reverseRecursively(head->next);
head->next->next = head;
head->next = NULL;

return rest;
}
int main() {
struct Node* head = NULL;
clrscr();
insertAtEnd(&head, 1);
insertAtEnd(&head, 2);
insertAtEnd(&head, 3);
insertAtEnd(&head, 4);
insertAtEnd(&head, 5);

printf("Original List: ");


printList(head);

// Reverse iteratively
reverseIteratively(&head);
printf("Reversed List (Iterative): ");
printList(head);

// Reverse back using recursion to get original list


head = reverseRecursively(head);
printf("Reversed List (Recursive): ");
printList(head);
getche();
return 0;
}
Output:
Original List: 1 → 2 → 3 → 4 →5 → NULL
Reversed List(Iterative): 5 → 4 → 3 → 2 →1 → NULL
Reversed List(Recursive): 1 → 2 → 3 → 4 →5 → NULL
Result: Hence, a program to reverse a linked list iteratively and recursively is successfully
executed

iii) Solve problems involving linked list traversal and manipulation.


AIM: To Solve problems involving linked list traversal and manipulation.
Description:
Traversing a Linked List: A linked list is a linear data structure that needs to be traversed
starting from the head node until the end of the list. Unlike arrays, where random access is
possible, linked list requires access to its nodes through sequential traversal.Traversing a linked
list is important in many applications. For example, we may want to print a list or search for a
specific node in the list. Or we may want to perform an advanced operation on the list as we
traverse the list. The algorithm for traversing a list is fairly trivial.
a. Start with the head of the list. Access the content of the head node if it is not null.
b. Then go to the next node(if exists) and access the node information
c. Continue until no more nodes (that is, you have reached the last node)
Program:
#include <stdio.h>
#include <stdlib.h>

// Define the Node structure


typedef struct Node {
int data;
struct Node* next;
} Node;

// Function to create a new node


Node* createNode(int data) {
Node* newNode = (Node*) malloc(sizeof(Node));
if (newNode == NULL) {
printf("Error creating a new node.\n");
exit(0);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}

// Function to insert a node at the beginning of the list


void insertAtBeginning(Node** head, int data) {
Node* newNode = createNode(data);
newNode->next = *head;
*head = newNode;
}

// Function to insert a node at the end of the list


void insertAtEnd(Node** head, int data) {
Node* newNode = createNode(data);
Node* lastNode;
if (*head == NULL) {
*head = newNode;
return;
}
lastNode = *head;
while (lastNode->next != NULL) {
lastNode = lastNode->next;
}
lastNode->next = newNode;
}

// Function to delete the first node


void deleteFirstNode(Node** head) {
Node* toDelete;
if (*head == NULL) {
printf("List is already empty.\n");
return;
}
toDelete = *head;
*head = (*head)->next;
free(toDelete);
}

// Function to delete a node by its value


void deleteNodeByValue(Node** head, int value) {
Node *current = *head, *previous = NULL;

if (current != NULL && current->data == value) {


*head = current->next;
free(current);
return;
}

while (current != NULL && current->data != value) {


previous = current;
current = current->next;
}

if (current == NULL) {
printf("Value not found in the list.\n");
return;
}

previous->next = current->next;
free(current);
}

// Function to print the linked list


void printList(Node* head) {
Node* current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}

// Function to count the number of nodes in the linked list


int countNodes(Node* head) {
int count = 0;
Node* current = head;
while (current != NULL) {
count++;
current = current->next;
}
return count;
}

// Main function to test the linked list operations


int main() {
Node* head = NULL; // Start with an empty list
clrscr();
// Demonstrating insertion
insertAtBeginning(&head, 20);
insertAtBeginning(&head, 10);
insertAtEnd(&head, 30);
insertAtEnd(&head, 40);
printf("Linked List: ");
printList(head);

// Display the count of nodes


printf("Total nodes count: %d\n", countNodes(head));

// Demonstrating deletion
deleteFirstNode(&head);
printf("After deleting the first node: ");
printList(head);

deleteNodeByValue(&head, 30);
printf("After deleting a node with value 30: ");
printList(head);

// Final node count


printf("Total nodes count after deletions: %d\n", countNodes(head));
getch();
return 0;
}
Output:
Linked List: 10→20→30→40→NULL
Total nodes count: 4
After deleting the first node: 20→30→40→NULL
After deleting a node with value 30: 20→40→NULL
Total nodes count after deletions: 2
Result: Hence, the problems involving linked list traversal and manipulation has been
executed successfully.

Experiment - 3
Linked List Applications

i) Create a program to detect and remove duplicates from a linked list


AIM: To Create a program to detect and remove duplicates from a linked list
Description:
To detect duplicates from linked list first we traverse the whole linked list. For each node we
check in the remaining list whether the duplicate node exists or not. If it does then we increment
the count. Duplicates can be removed by a naive approach. The naive approach is to iterate
through the linked list and keep comparing the current node with its next node, and if they are
the same, delete the next node and move to the next node.
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

// Define the Node structure


struct Node {
int data;
struct Node* next;
};

// Function to add a new node at the end of the list


void push(struct Node** head, int data) {
struct Node* newnode = (struct Node*) malloc(sizeof(struct Node));
struct Node* last = *head;

newnode->data = data;
newnode->next = NULL;

if (*head == NULL) {
*head = newnode;
return;
}

while (last->next != NULL) {


last = last->next;
}

last->next = newnode;
}

// Function to remove duplicates from the list


void removeDuplicates(struct Node* head) {
struct Node *ptr1, *ptr2, *dup;
ptr1 = head;

// Pick elements one by one


while (ptr1 != NULL && ptr1->next != NULL) {
ptr2 = ptr1;

// Compare the picked element with the rest of the elements


while (ptr2->next != NULL) {
// If duplicate, then delete it
if (ptr1->data == ptr2->next->data) {
dup = ptr2->next;
ptr2->next = ptr2->next->next;
free(dup);
} else {
ptr2 = ptr2->next; // This is how we move on if no deletion happens
}
}
ptr1 = ptr1->next;
}
}
// Function to print the nodes in a given linked list
void printList(struct Node* head) {
while ( head != NULL) {
printf("%d ", head->data);
head = head->next;
}
printf("\n");
}

int main() {
struct Node* head = NULL;
clrscr();
// Creating the list: 10->20->20->30->40->30->NULL
push(&head, 10);
push(&head, 20);
push(&head, 20);
push(&head, 30);
push(&head, 40);
push(&head, 30);
printf("Original linked list:\n");
printList(head);
removeDuplicates(head);
printf("Linked list after removing duplicates:\n");
printList(head);
getch();
return 0;
}
Output:
Original linked list:
10 20 20 30 40 30
Linked list after removing duplicates:
10 20 30 40
Result:
Hence , c program to detect and remove duplicates from a linked list is executed successfully.
ii) To Implement a linked list to represent polynomials and perform addition.
AIM: To Implement a linked list to represent polynomials and perform addition.
Description:
Polynomial addition in c programming is a fundamental operation in mathematics and
computer science, widely used in various applications such as signal processing, computer
graphics, and scientific simulations. We will do the polynomial addition using linked list in C
programming. When dealing with polynomials, it is essential to have an efficient and flexible
data structure to represent and perform operations on them. Linked lists provide an elegant
solution for efficiently handling polynomials due to their dynamic memory allocation and
straightforward implementation.
Some helpful Observations which will aid us in performing the Polynomial Addition using
Linked List in C are:
• We only added the coefficients of the like terms (terms having the same variables and
the same degree).
• The terms are placed in descending order by degree.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Node structure to store each term of a polynomial
struct Node {
int coeff;
int exp;
struct Node* next;
};

// Function to create a new node


void insertTerm(struct Node** poly, int coeff, int exp) {
struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = *poly;

newnode->coeff = coeff;
newnode->exp = exp;
newnode->next = NULL;
// If the polynomial is empty, insert the new term as the first term
if (*poly == NULL) {
*poly = newnode;
} else {
// Move to the end of the polynomial
while (last->next != NULL) {
last = last->next;
}
// Add the new term at the end of the polynomial
last->next = newnode;
}
}
// Function to add two polynomials
struct Node* addPolynomials(struct Node* poly1, struct Node* poly2) {
struct Node* result = NULL;
while (poly1 != NULL && poly2 != NULL) {
// If the exponents are the same, add the coefficients
if (poly1->exp == poly2->exp) {
insertTerm(&result, poly1->coeff + poly2->coeff, poly1->exp);
poly1 = poly1->next;
poly2 = poly2->next;
}
// If poly1's exponent is greater, add poly1's term to the result
else if (poly1->exp > poly2->exp) {
insertTerm(&result, poly1->coeff, poly1->exp);
poly1 = poly1->next;
}
// If poly2's exponent is greater, add poly2's term to the result
else {
insertTerm(&result, poly2->coeff, poly2->exp);
poly2 = poly2->next;
}
}
// If there are remaining terms in poly1, add them to the result
while (poly1 != NULL) {
insertTerm(&result, poly1->coeff, poly1->exp);
poly1 = poly1->next;
}
// If there are remaining terms in poly2, add them to the result
while (poly2 != NULL) {
insertTerm(&result, poly2->coeff, poly2->exp);
poly2 = poly2->next;
}
return result;
}
// Function to print the polynomial
void printPolynomial(struct Node* head) {
while (head != NULL) {
printf("%dx^%d", head->coeff, head->exp);
head = head->next;
if (head != NULL)
printf(" + ");
}
printf("\n");
}

int main() {
struct Node* poly1 = NULL;
struct Node* poly2 = NULL;
struct Node* result = NULL;
clrscr();
// Insert terms into the first polynomial: 5x^2 + 4x^1 + 2
insertTerm(&poly1, 5, 3);
insertTerm(&poly1, 4, 2);
insertTerm(&poly1, 2, 1);
// Insert terms into the second polynomial: -5x^1 - 2
insertTerm(&poly2, -3, 2);
insertTerm(&poly2, 2, 1);
printf("Polynomial 1: ");
printPolynomial(poly1);
printf("Polynomial 2: ");
printPolynomial(poly2);
// Add the polynomials
result = addPolynomials(poly1, poly2);
printf("Resultant Polynomial: ");
printPolynomial(result);
getch();
return 0;
}
Output:
Polynomial 1: 5x^3 + 4x^2 + 2x^1
Polynomial 2: -3x^2 + 2x^1
Resultant Polynomial: 5x^3 + 1x^2 + 4x^1
Result:
Hence, a linked list to represent polynomials and perform addition has been implemented
successfully.

iii) Aim: To Implement a double-ended queue (deque) with essential operations.

Description:
The dequeue represents Double Ended Queue. In the queue, the inclusion happens
from one end while the erasure happens from another end. The end at which the addition
happens is known as the backside while the end at which the erasure happensis known as front
end. Deque is a direct information structure in which the inclusion and cancellation tasks are
performed from the two finishes. We can say that deque is a summed up form of the line.
How about we take a gander at certain properties of deque. Deque can be utilized both as stack
and line as it permits the inclusion and cancellation procedure on the two finishes. In deque,
the inclusion and cancellation activity can be performed from one side. The stack adheres to
the LIFO rule in which both the addition and erasure can be performed distinctly from one end;
in this way, we reason that deque can be considered as a stack.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define MAX 10

int deque[MAX];
int front = -1, rear = -1;

// Function to check if deque is full or not


int isFull() {
return ((front == 0 && rear == MAX - 1) || front == rear + 1);
}
// Function to check if deque is empty
int isEmpty() {
return (front == -1);
}
// Function to insert at the front of the deque
void insert_front(int value) {
if (isFull()) {
printf("Deque is full.\n");
return;
}
if (front == -1) { // If deque is initially empty
front = rear = 0;
} else if (front == 0) {
front = MAX - 1; // Circular increment
} else {
front = front - 1;
}
deque[front] = value;
printf("inserted %d at front\n",value);
}

// Function to insert at the rear of the deque


void insert_rear(int value) {
if (isFull()) {
printf("Deque is full.\n");
return;
}
if (rear == -1) { // If deque is initially empty
front = rear = 0;
} else if (rear == MAX - 1) {
rear = 0; // Circular increment
} else {
rear = rear + 1;
}
deque[rear] = value;
printf("inserted %d at rear\n",value);
}
// Function to delete from the front of the deque
int delete_front() {
int data;
if (isEmpty()) {
printf("Deque is empty.\n");
return -1;
}
data = deque[front];
if (front == rear) { // Single element
front = rear = -1;
} else if (front == MAX - 1) {
front = 0;
} else {
front = front + 1;
}
printf("After deleting element from front\n");
return data;
}
// Function to delete from the rear of the deque
int delete_rear() {
int data;
if (isEmpty()) {
printf("Deque is empty.\n");
return -1;
}
data = deque[rear];
if (front == rear) { // Single element
front = rear = -1;
} else if (rear == 0) {
rear = MAX - 1;
} else {
rear = rear - 1;
}
printf("After deleting element from rear\n");
return data;
}
// Function to display the elements of the deque
void display() {
int i;
if (isEmpty()) {
printf("Deque is empty.\n");
return;
}
printf("Deque elements: ");
i = front;
while (1) {
printf("%d ", deque[i]);
if (i == rear) {
break;
}
i = (i + 1) % MAX;
}
printf("\n");
}
int main() {
clrscr();
insert_rear(5);
insert_front(10);
insert_rear(15);
display();
delete_front();
display();
insert_front(20);
display();
delete_rear();
display();
getche();
return 0;
}
Output:
Inserted 5 at rear
Inserted 10 at front
Inserted 15 at rear
Deque elements: 10 5 15
After deleting element from front
Deque elements: 5 15
Inserted 20 at front
Deque elements: 20 5 15
After deleting element from rear
Deque elements: 20 5
Result: Hence,a double ended queue with essential operations is implemented successfully.

Experiment-4
Double Linked List Implementation

i) Aim: Implement a doubly linked list and perform various operations to understand
it’s properties and applications.
Description: Doubly linked list is a complex type of linked list in which a node contains a
pointer to the previous as well as the next node in the sequence. Therefore, in a doubly linked
list, a node consists of three parts: node data, pointer to the next node in sequence (next pointer)
, pointer to the previous node (previous pointer). A doubly linked list containing three nodes
having numbers from 1 to 3 in their data part The prev part of the first node and the next part
of the last node will always contain null indicating end in each direction.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Node structure for a doubly linked list
struct Node {
int data;
struct Node* prev;
struct Node* next;
};

// Function to create a new node


struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed.\n");
exit(1);
}
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}

// Function to insert a node at the beginning of the doubly linked list


struct Node* insertAtBeginning(struct Node* head, int data) {
struct Node* newNode = createNode(data);
if (head == NULL) {
head = newNode;
printf("Inserted %d at the beginning.\n", data);
} else {
newNode->next = head;
head->prev = newNode;
head = newNode;
printf("Inserted %d at the beginning.\n", data);
}
return head;
}

// Function to insert a node at the end of the doubly linked list


struct Node* insertAtEnd(struct Node* head, int data) {
struct Node* newNode = createNode(data);
if (head == NULL) {
head = newNode;
} else {
struct Node* temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
newNode->prev = temp;
printf("Inserted %d at the end.\n", data);
}
return head;
}

// Function to delete a node with given data from the doubly linked list
struct Node* deleteNode(struct Node* head, int data) {
if (head == NULL) {
printf("List is empty. Cannot delete.\n");
} else {
struct Node* current = head;
struct Node* prevNode = NULL;
while (current != NULL && current->data != data) {
prevNode = current;
current = current->next;
}
if (current == NULL) {
printf("Node with data %d not found.\n", data);
} else {
if (prevNode == NULL) {
head = current->next;
} else {
prevNode->next = current->next;
if (current->next != NULL) {
current->next->prev = prevNode;
}
}
free(current);
printf("Node with data %d deleted.\n", data);
}
}
return head;
}
// Function to traverse and display the doubly linked list
void displayList(struct Node* head) {
if (head == NULL) {
printf("List is empty.\n");
} else {
printf("Doubly linked list: ");
while (head != NULL) {
printf("%d ", head->data);
head = head->next;
}
printf("\n");
}
}
int main() {
struct Node* head = NULL;
clrscr();
// Inserting elements into the doubly linked list
head = insertAtBeginning(head, 10);
head = insertAtEnd(head, 20);
head = insertAtBeginning(head, 5);
head = insertAtEnd(head, 30);
displayList(head);
head = deleteNode(head, 20);
displayList(head);
getche();
return 0;
}
Output:
Inserted 10 at the beginning.
Inserted 20 at the end.
Inserted 5 at the beginning.
Inserted 30 at the end.
Doubly linked list: 5 10 20 30
Node with data 20 deleted.
Doubly linked list: 5 10 30
Result:
Hence, a doubly linked list and perform various operations to understand itsproperties and
applications is successfully implemented.

ii) Aim: To implement a circular linked list and perform insertion, deletion , and
traversal.
Description: Circular Linked List is a variation of Linked list in which the first element points
to the last element and the last element points to the first element. Both Singly Linked List and
Doubly Linked List can be made into a circular linked list. n singly linked list, the next pointer
of the last node points to the first node. In doubly linked list, the next pointer of the last node
points to the first node and the previous pointer of the first node points to the last node making
the circular in both directions.
• The last link's next points to the first link of the list in both cases of singly as well as
doubly linked list.
• The first link's previous points to the last of the list in case of doubly linked list.
Following are the important operations supported by a circular list.
• insert − Inserts an element at the start of the list.
• delete − Deletes an element from the start of the list.
• display − Displays the list.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Node structure for a circular linked list
struct Node {
int data;
struct Node* next;
};
// Function to create a new node
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed.\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// Function to insert a node at the beginning of the circular linked list
struct Node* insertAtBeginning(struct Node* head, int data) {
struct Node* newNode = createNode(data);
if (head == NULL) {
head = newNode;
newNode->next = head; // Circular link to itself
printf("Inserted %d at the beginning.\n", data);
} else {
struct Node* temp = head;
while (temp->next != head) {
temp = temp->next;
}
temp->next = newNode;
newNode->next = head;
head = newNode;
printf("Inserted %d at the beginning.\n", data);
}
return head;
}
// Function to insert a node at the end of the circular linked list
struct Node* insertAtEnd(struct Node* head, int data) {
struct Node* newNode = createNode(data);
if (head == NULL) {
head = newNode;
newNode->next = head; // Circular link to itself
} else {
struct Node* temp = head;
while (temp->next != head) {
temp = temp->next;
}
temp->next = newNode;
newNode->next = head;
printf("Inserted %d at the end.\n", data);
}
return head;
}
// Function to delete a node with given data from the circular linked list
struct Node* deleteNode(struct Node* head, int data) {
if (head == NULL) {
printf("List is empty. Cannot delete.\n");
} else {
struct Node* temp = head;
struct Node* prevNode = NULL;
do {
if (temp->data == data) {
if (prevNode == NULL) { // If the node to be deleted is the head node
while (temp->next != head) {
temp = temp->next;
}
temp->next = head->next;
free(head);
head = temp->next;
break;
} else {
prevNode->next = temp->next;
free(temp);
break;
}
}
prevNode = temp;
temp = temp->next;
} while (temp != head);
if (temp == head) {
printf("Node with data %d not found.\n", data);
}
printf("Node with data %d deleted.\n", data);
}
return head;
}
// Function to traverse and display the circular linked list
void displayList(struct Node* head) {
if (head == NULL) {
printf("List is empty.\n");
} else {
struct Node* temp = head;
printf("Circular linked list: ");
do {
printf("%d ", temp->data);
temp = temp->next;
} while (temp != head);
printf("\n");
}
}
int main() {
struct Node* head = NULL;
clrscr();
// Inserting elements into the circular linked list
head = insertAtBeginning(head, 10);
head = insertAtEnd(head, 20);
head = insertAtBeginning(head, 5);
head = insertAtEnd(head, 30);
displayList(head);
head = deleteNode(head, 20);
displayList(head);
getche();
return 0;
}
Output:
Inserted 10 at the beginning
Inserted 20 at the end
Inserted 5 at the beginning
Inserted 30 at the end
Circular linked list: 5 10 20 30
Node with data 20 deleted
Circular linked list: 5 10 30
Result:
Hence, a circular linked list and perform insertion, deletion, and traversal is implemented
successfully.

Experiment- 5
Stack Operations
i) Aim: To Implement a stack using arrays
Description: A stack is a linear data structure in which the insertion of a new element and
removal of an existing element takes place at the same end represented as the top of the stack.
To implement the stack, it is required to maintain the pointer to the top of the stack, which is
the last element to be inserted because we can access the elements only on the top of the
stack.In an array-based implementation, the push operation is implemented by incrementing
the index of the top element and storing the new element at that index. The pop operation is
implemented by storing the element at the top, decrementing the index of the top element and
returning the value stored.
Program:
Stack using arrays:
#include <stdio.h>
#include <conio.h>
#define MAX_SIZE 10
// Structure to represent a stack
struct Stack {
int arr[MAX_SIZE];
int top;
};
// Function to initialize the stack
void initializeStack(struct Stack *stack) {
stack->top = -1;
}
// Function to check if the stack is full
int isFull(struct Stack *stack) {
return stack->top == MAX_SIZE - 1;
}
// Function to check if the stack is empty
int isEmpty(struct Stack *stack) {
return stack->top == -1;
}
// Function to push an element onto the stack
void push(struct Stack *stack, int value) {
if (isFull(stack)) {
printf("Stack overflow. Cannot push %d\n", value);
} else {
stack->arr[++stack->top] = value;
printf("Pushed %d onto the stack\n", value);
}
}
// Function to pop an element from the stack
int pop(struct Stack *stack) {
if (isEmpty(stack)) {
printf("Stack underflow. Cannot pop from an empty stack\n");
return -1; // Return a sentinel value for an empty stack
} else {
return stack->arr[stack->top--];
}
}
// Function to display the elements in the stack
void display(struct Stack *stack) {
int i;
if (isEmpty(stack)) {
printf("Stack is empty.\n");
} else {
for (i = 0; i <= stack->top; ++i) {
printf("%d ", stack->arr[i]);
}
printf("\n");
}
}
int main() {
struct Stack myStack;
int poppedValue;
clrscr();
initializeStack(&myStack);
// Push elements onto the stack
push(&myStack, 10);
push(&myStack, 20);
push(&myStack, 30);
push(&myStack, 40);
push(&myStack, 50);
// Display the stack
printf("Stack after pushes: ");
display(&myStack);
// Pop an element from the stack
poppedValue = pop(&myStack);
printf("Popped value: %d\n", poppedValue);
printf("Popped value: %d \n",pop(&myStack));
printf("Popped value: %d\n", pop(&myStack));
// Display the stack after pop
printf("Stack after pop: ");
display(&myStack);
getche();
return 0;
}
Output:
Pushed 10 onto the stack
Pushed 20 onto the stack
Pushed 30 onto the stack
Pushed 40 onto the stack
Pushed 50 onto the stack
Stack after pushes: 10 20 30 40 50
Popped value: 50
Popped value: 40
Popped value: 30
Stack after pop: 10 20
Result: Hence, a stack using arrays is implemented successfully.
Stack using Linked list:
Aim: To Implement a stack using linked lists.
Description: In a linked list-based implementation, the push operation is implemented by
creating a new node with the new element and setting the next pointer of the current top node
to the new node. The pop operation is implemented by setting the next pointer of the current
top node to the next node and returning the value of the current top node.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// Structure to represent a node in the stack
struct Node {
int data;
struct Node* next;
};
// Structure to represent the stack
struct Stack {
struct Node* top;
};
// Function to initialize the stack
void initializeStack(struct Stack* stack) {
stack->top = NULL; // Initialize top to NULL to indicate an empty stack
}
// Function to check if the stack is empty
int isEmpty(struct Stack* stack) {
return stack->top == NULL;
}
// Function to push an element onto the stack
void push(struct Stack* stack, int element) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed! Cannot push %d onto the stack.\n", element);
return;
}

newNode->data = element;
newNode->next = stack->top;
stack->top = newNode;

printf("Pushed %d onto the stack.\n", element);


}
// Function to pop an element from the stack
int pop(struct Stack* stack) {
struct Node* poppedNode;
int poppedElement;
if (isEmpty(stack)) {
printf("Stack underflow! Cannot pop from an empty stack.\n");
return -1; // Return an error value
}
poppedNode = stack->top;
poppedElement = poppedNode->data;
stack->top = poppedNode->next;
free(poppedNode);
return poppedElement;
}
// Function to display elements in the stack
void display(struct Stack* stack) {
struct Node* current = stack->top;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
struct Stack stack;
initializeStack(&stack);
clrscr();
// Push elements onto the stack
push(&stack, 5);
push(&stack, 10);
push(&stack, 15);
// Display elements in the stack
printf("Stack elements: ");
display(&stack);
// Pop elements from the stack and display
printf("Popped element: %d\n", pop(&stack));
printf("Popped element: %d\n", pop(&stack));
// Display remaining elements in the stack
printf("Stack elements after popping: ");
display(&stack);
getche();
return 0;
}
Output:
Pushed 5 onto the stack
Pushed 10 onto the stack
Pushed 15 onto the stack
Stack elements: 15 10 5
Popped element: 15
Popped element: 10
Stack elements after popping: 5
Result:
Hence, a stack using linked lists is implemented successfully.

ii) Aim: Write a program to evaluate a postfix expression using a stack.


Description: To evaluate a postfix expression we can use a stack.Iterate the expression from
left to right and keep on storing the operands into a stack. Once an operator is received, pop
the two topmost elements and evaluate them and push the result in the stack again.
The algorithm for postfix evaluation is :
• Read a character
• Convert to int and push
• If the character is an operator pop the stack twice to obtain two operands
• Push the outcome of operation
• Remove the final value from the stack
Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#define MAX 100

// Stack structure
typedef struct {
int top;
int items[MAX];
} Stack;
// Function to initialize the stack
void initStack(Stack *s) {
s->top = -1;
}
// Check if the stack is empty
int isEmpty(Stack *s) {
return s->top == -1;
}
// Check if the stack is full
int isFull(Stack *s) {
return s->top == MAX - 1;
}
// Push an element onto the stack
void push(Stack *s, int value) {
if (isFull(s)) {
printf("Stack is full\n");
return;
}
s->items[++(s->top)] = value;
}
// Pop an element from the stack
int pop(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty\n");
return -1; // Returning -1 to indicate error
}
return s->items[(s->top)--];
}
// Function to evaluate postfix expression
int evaluatePostfix(char *exp) {
Stack s;
int i,val1,val2;
initStack(&s);
for (i = 0; exp[i]; ++i) {
// If the scanned character is an operand (number here),
// push it to the stack.
if (isdigit(exp[i])) {
push(&s, exp[i] - '0'); // Convert char to int
}
// If the scanned character is an operator, pop two
// elements from stack apply the operator
else {
val1 = pop(&s);
val2 = pop(&s);
switch (exp[i]) {
case '+': push(&s, val2 + val1); break;
case '-': push(&s, val2 - val1); break;
case '*': push(&s, val2 * val1); break;
case '/': push(&s, val2 / val1); break;
}
}
}
return pop(&s); // Return the result of the expression
}
int main() {
char exp[] = "231*+9-";
clrscr();
printf("postfix evaluation: %s = %d\n", exp, evaluatePostfix(exp));
getche();
return 0;
}
Output:
Postfix evaluation: 231*+9- = -4
Result:
Hence, a program to evaluate a postfix expression using a stack is successfully executed

iii) Aim: Implement a program to check for balanced parentheses using a stack.
Description: To check for balanced brackets in an expression using stack in C programming,
you need to traverse the expression and keep track of the opening and closing parentheses using
a stack data structure. If an opening parenthesis is encountered, it is pushed onto the stack, and
if a closing parenthesis is encountered, it is compared with the top element of the stack.If the
closing parenthesis matches the top element, the top element is popped, and the traversal
continues. If the closing parenthesis does not match the top element or there are no more
elements in the stack, the expression is considered unbalanced.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define MAX 100

typedef struct {
int top;
char items[MAX];
} Stack;
void initStack(Stack *s) {
s->top = -1;
}
int isEmpty(Stack *s) {
return s->top == -1;
}
int isFull(Stack *s) {
return s->top == MAX - 1;
}
void push(Stack *s, char value) {
if (isFull(s)) {
printf("Stack is full\n");
return;
}
s->items[++(s->top)] = value;
}
char pop(Stack *s) {
if (isEmpty(s)) {
return -1; // Return -1 to indicate error/empty
}
return s->items[(s->top)--];
}
// Function to check if the characters are matching left and right parentheses
int isMatch(char character1, char character2) {
return (character1 == '(' && character2 == ')') ||
(character1 == '{' && character2 == '}') ||
(character1 == '[' && character2 == ']');
}
// Function to check if parentheses are balanced
int areParenthesesBalanced(char expression[]) {
Stack s;
int i;
initStack(&s);
for (i = 0; i < strlen(expression); i++) {
// If the character is an opening parenthesis, push it onto the stack
if (expression[i] == '(' || expression[i] == '{' || expression[i] == '[') {
push(&s, expression[i]);
} else if (expression[i] == ')' || expression[i] == '}' || expression[i] == ']') {
// If the stack is empty or parentheses do not match
if (isEmpty(&s) || !isMatch(pop(&s), expression[i])) {
return 0; // Not balanced
}
}
}
// If the stack is empty, parentheses are balanced
return isEmpty(&s);
}
int main() {
char expression[30];
clrscr();
printf("Input an expression in parentheses:");
scanf("%s",expression);

if (areParenthesesBalanced(expression))
printf("Balanced\n");
else
printf("Not Balanced\n");
getche();
return 0;
}
Output:
Input an expression in parentheses: ({}[])
Balanced
Result:
Hence, a program to check for balanced parentheses using a stack is successfully
executed.

Experiment – 6
Queue Operations

i. Aim: To Implement a queue using arrays.


Description: We can easily represent queue by using linear arrays. There are two variables i.e.
front and rear, that are implemented in the case of every queue. Front and rear variables point
to the position from where insertions and deletions are performed in a queue. Initially, the value
of front and queue is -1 which represents an empty queue.
To implement a queue using an array,
• create an array arr of size n and
• take two variables front and rear both of which will be initialized to 0 which means the
queue is currently empty.
• Element
• rear is the index up to which the elements are stored in the array and
• front is the index of the first element of the array.
Now, some of the implementations of queue operations are as follows: Enqueue, Dequeue,
Front, Display
Queue using arrays:
Program:
#include <stdio.h>
#include <conio.h>
#define MAX_SIZE 100

typedef struct {
int arr[MAX_SIZE];
int front, rear;
} Queue;
// Initialize the queue
void initialize(Queue *queue) {
queue->front = -1; // Initialize front
queue->rear = -1; // Initialize rear
}
// Check if the queue is empty
int isEmpty(Queue *queue) {
return (queue->front == -1 && queue->rear == -1);
}
// Check if the queue is full
int isFull(Queue *queue) {
return (queue->rear + 1) % MAX_SIZE == queue->front;
}
// Enqueue (add element to the queue)
void enqueue(Queue *queue, int value) {
if (isFull(queue)) {
printf("Queue is full! Cannot enqueue.\n");
return;
} else if (isEmpty(queue)) {
queue->front = 0; // If empty, set front to 0
}
queue->rear = (queue->rear + 1) % MAX_SIZE;
queue->arr[queue->rear] = value;
printf("Enqueued %d to the queue.\n", value);
}
// Dequeue (remove element from the queue)
int dequeue(Queue *queue) {
int dequeuedValue;
if (isEmpty(queue)) {
printf("Queue is empty! Cannot dequeue.\n");
return -1; // Return an error value
}
dequeuedValue = queue->arr[queue->front];
if (queue->front == queue->rear) {
// If only one element is present, reset front and rear
queue->front = -1;
queue->rear = -1;
} else {
queue->front = (queue->front + 1) % MAX_SIZE;
}
printf("Dequeued %d from the queue.\n", dequeuedValue);
return dequeuedValue;
}
// Get the front element of the queue
int front(Queue *queue) {
if (isEmpty(queue)) {
printf("Queue is empty!\n");
return -1;
}
return queue->arr[queue->front];
}
int main() {
Queue queue;
clrscr();
initialize(&queue);
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
printf("Front element: %d\n", front(&queue)); // Output: 10
dequeue(&queue); // Output: Dequeued 10 from the queue.
printf("Front element after dequeue: %d\n", front(&queue)); // Output: 20
getche();
return 0;
}
Output:
Enqueued 10 to the queue
Enqueued 20 to the queue
Enqueued 30 to the queue
Front element: 10
Dequeued 10 from the queue
Front element after dequeue: 20
Result:
Hence, a Queue using arrays is implemented successfully.

Queue using Linked list:


Aim: Implement a queue using linked lists.
Description: The array implementation of Queue can not be used for the large scale
applications where the queues are implemented. One of the alternative of array implementation
is linked list implementation of queue.The storage requirement of linked representation of a
queue with n elements is o(n) while the time requirement for operations is o(1).In a linked
queue, each node of the queue consists of two parts i.e. data part and the link part. Each element
of the queue points to its immediate next element in the memory.In the linked queue, there are
two pointers maintained in the memory i.e. front pointer and rear pointer. The front pointer
contains the address of the starting element of the queue while the rear pointer contains the
address of the last element of the queue.
Program:
#include <stdio.h>
#include <stdlib.h>

// Node structure for the queue


typedef struct Node {
int data;
struct Node* next;
} Node;
// Queue structure
typedef struct {
Node* front; // Pointer to the front of the queue
Node* rear; // Pointer to the rear of the queue
} Queue;
// Initialize the queue
void initialize(Queue* queue) {
queue->front = NULL;
queue->rear = NULL;
}
// Check if the queue is empty
int isEmpty(Queue* queue) {
return (queue->front == NULL);
}
// Enqueue (add element to the queue)
void enqueue(Queue* queue, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failed!\n");
return;
}
newNode->data = value;
newNode->next = NULL;

if (isEmpty(queue)) {
queue->front = newNode;
} else {
queue->rear->next = newNode;
}
queue->rear = newNode;
printf("Enqueued %d to the queue.\n", value);
}
// Dequeue (remove element from the queue)
int dequeue(Queue* queue) {
int dequeuedValue;
struct Node* temp;
if (isEmpty(queue)) {
printf("Queue is empty! Cannot dequeue.\n");
return -1; // Return an error value
}
dequeuedValue = queue->front->data;
temp = queue->front;
queue->front = queue->front->next;
free(temp);
if (queue->front == NULL) {
queue->rear = NULL; // Adjust rear if queue becomes empty
}
printf("Dequeued %d from the queue.\n", dequeuedValue);
return dequeuedValue;
}
// Get the front element of the queue
int front(Queue* queue) {
if (isEmpty(queue)) {
printf("Queue is empty!\n");
return -1; // Return an error value
}
return queue->front->data;
}
int main() {
Queue queue;
clrscr();
initialize(&queue);
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
printf("Front element: %d\n", front(&queue));
dequeue(&queue);
printf("Front element after dequeue: %d\n", front(&queue));
getche();
return 0;
}
Output:
Enqueued 10 to the queue
Enqueued 20 to the queue
Enqueued 30 to the queue
Front element: 10
Dequeued 10 from the queue
Front element after dequeue: 20
Result:
Hence, a Queue using linked lists is implemented successfully.

ii. Aim: Program to implement simple printer queue system


Description:
A simple printer queue system is designed to manage print jobs submitted by multiple users in
an orderly manner. It operates on a first-come, first-served basis, where each new job is added
to the end of the queue. The system processes and completes each print job sequentially from
the front of the queue. This setup ensures fairness and efficiency in handling multiple print
requests. The system also allows for basic operations like adding a job to the queue, viewing
the queue, and removing a job once it's completed.
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

typedef struct PrintJob {


int jobId;
char docName[100];
struct PrintJob* next;
} PrintJob;

typedef struct {
PrintJob* front;
PrintJob* rear;

int count;
} Queue;
// Function to create a new print job node
PrintJob* createPrintJob(int id, const char* docName) {
PrintJob* newJob = (PrintJob*)malloc(sizeof(PrintJob));
if (newJob == NULL) {
printf("Error allocating memory.\n");
return NULL;
}
newJob->jobId = id;
strcpy(newJob->docName, docName);
newJob->next = NULL;
return newJob;
}

// Initialize the queue


void initializeQueue(Queue* q) {
q->front = q->rear = NULL;
q->count = 0;
}

// Check if the queue is empty


int isEmpty(Queue* q) {
return q->front == NULL;
}

// Add a job to the queue


void enqueue(Queue* q, PrintJob* job) {
if (q->rear == NULL) {
q->front = q->rear = job;
} else {
q->rear->next = job;
q->rear = job;
}
q->count++;
printf("Job %d enqueued successfully.\n", job->jobId);
}

// Remove a job from the queue


PrintJob* dequeue(Queue* q) {
PrintJob* temp;
if (isEmpty(q)) {
printf("Queue is empty. No jobs to process.\n");
return NULL;
}
temp = q->front;
q->front = q->front->next;
if (q->front == NULL)
q->rear = NULL;
q->count--;
printf("Job %d dequeued.\n", temp->jobId);
return temp;
}

// Display the printer queue


void displayQueue(Queue* q) {
PrintJob* temp;
if (isEmpty(q)) {
printf("Queue is empty.\n");
return;
}
temp = q->front;
printf("Current Printer Queue:\n");
while (temp != NULL) {
printf("Job ID: %d, Document: %s\n", temp->jobId, temp->docName);
temp = temp->next;
}
}

// Main function to test the printer queue


int main() {
Queue printerQueue;
PrintJob* processedJob;
clrscr();
initializeQueue(&printerQueue);

enqueue(&printerQueue, createPrintJob(1, "file1.pdf"));


enqueue(&printerQueue, createPrintJob(2, "report.docx"));
enqueue(&printerQueue, createPrintJob(3, "image.jpg"));

displayQueue(&printerQueue);

processedJob = dequeue(&printerQueue);
free(processedJob); // Free memory after processing

displayQueue(&printerQueue);
getch();
return 0;
}
Output:
Job 1 enqueued successfully.
Job 2 enqueued successfully.
Job 3 enqueued successfully.
Current Printer Queue:
Job ID: 1, Document: file1.pdf
Job ID: 2, Document: report.docx
Job ID: 3, Document: image.jpg
Job 1 dequeued.
Current Printer Queue:
Job ID: 2, Document: report.docx
Job ID: 3, Document: image.jpg

iii. Aim: To Solve problems involving circular queues.


Description: A circular queue is the extended version of a regular queue where the last element
is connected to the first element. Thus forming a circle-like structure. The circular queue solves
the major limitation of the normal queue. In a normal queue, after a bit of insertion and deletion,
there will be non-usable empty space. Circular Queue works by the process of circular
increment i.e. when we try to increment the pointer and we reach the end of the queue, we start
from the beginning of the queue.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define QUEUE_SIZE 5

typedef struct {
int items[QUEUE_SIZE];
int front, rear;
} CircularQueue;
void initQueue(CircularQueue *q) {
q->front = -1;
q->rear = -1;
}
int isFull(CircularQueue *q) {
return (q->rear + 1) % QUEUE_SIZE == q->front;
}
int isEmpty(CircularQueue *q) {
return q->front == -1;
}
void enqueue(CircularQueue *q, int element) {
if (isFull(q)) {
printf("Queue is full!\n");
return;
}
if (isEmpty(q)) {
q->front = 0;
}
q->rear = (q->rear + 1) % QUEUE_SIZE;
q->items[q->rear] = element;
printf("Inserted -> %d\n", element);
}
int dequeue(CircularQueue *q) {
int element;
if (isEmpty(q)) {
printf("Queue is empty!\n");
return -1;
}
element = q->items[q->front];
if (q->front == q->rear) {
// Queue has only one element, so we reset the queue after removing it.
q->front = -1;
q->rear = -1;
} else {
q->front = (q->front + 1) % QUEUE_SIZE;
}
return element;
}
void printQueue(CircularQueue *q) {
int i;
if (isEmpty(q)) {
printf("Queue is empty!\n");
return;
}
printf("Queue elements: ");
i = q->front;
while (1) {
printf("%d ", q->items[i]);
if (i == q->rear)
break;
i = (i + 1) % QUEUE_SIZE;
}
printf("\n");
}
int main() {
CircularQueue q;
clrscr();
initQueue(&q);
enqueue(&q, 1);
enqueue(&q, 2);
enqueue(&q, 3);
enqueue(&q, 4);
enqueue(&q, 5);
// Trying to add an element should fail because the queue is full.
enqueue(&q, 6);
printQueue(&q);
printf("Dequeued: %d\n", dequeue(&q));
printf("Dequeued: %d\n", dequeue(&q));
printQueue(&q);
enqueue(&q, 6);
printQueue(&q);
getche();
return 0;
}
Output:
Inserted -> 1
Inserted -> 2
Inserted -> 3
Inserted -> 4
Inserted -> 5
Queue is full!
Queue elements: 1 2 3 4 5
Dequeued: 1
Dequeued: 2
Queue elements: 3 4 5
Inserted -> 6
Queue elements: 3 4 5 6
Result:
Hence, problems involving in circular queues are solved.

Experiment-7
Stack and Queue Applications
i. Aim: To Write a program using a stack to evaluate an infix expression and convert
it to postfix.
Description: The general mathematical way of representing algebraic expressions is to write
the operator between operands: Example: a + b. Such representation is called the Infix
representation. If we write the operator after the operands Example: a b +, it is called the Postfix
representation. It is also called the "Reverse Polish notation". Representing an algebraic
expression in postfix eliminates parentheses and the need of precedence. The evaluation
becomes simple. It is the ideal notation a computer uses to evaluate complex algebraic
expressions using stacks.
• An operator is printed after its operands in a postfix expression.
• In postfix notation, the infix expression 2+3 equals 23+.

Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#define MAX 100
/*Evaluating an infix expression and converting it to a postfix
expression using a stack in C involves two main steps:
1.Conversion of an Infix expression to a Postfix expression
2.Evaluation of the Postfix expression*/
typedef struct {
int top;
char items[MAX];
} Stack;

void initStack(Stack *s) {


s->top = -1;
}
int isEmpty(Stack *s) {
return s->top == -1;
}
void push(Stack *s, char value) {
if (s->top == (MAX - 1)) {
printf("Stack is full\n");
} else {
s->items[++(s->top)] = value;
}
}
char pop(Stack *s) {
if (isEmpty(s)) {
return -1;
} else {
return s->items[(s->top)--];
}
}
char peek(Stack *s) {
if (isEmpty(s)) return -1;
return s->items[s->top];
}
int isOperand(char ch) {
return isdigit(ch) || isalpha(ch);
}
int precedence(char op) {
switch (op) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
void infixToPostfix(char* infix, char* postfix) {
Stack s;
int i,j=0;
initStack(&s);
for (i = 0; infix[i]; i++) {
if (isOperand(infix[i])) {
postfix[j++] = infix[i];
} else if (infix[i] == '(') {
push(&s, infix[i]);
} else if (infix[i] == ')') {
while (!isEmpty(&s) && peek(&s) != '(') {
postfix[j++] = pop(&s);
}
pop(&s); // Remove '(' from stack
} else {
while (!isEmpty(&s) && precedence(infix[i]) <= precedence(peek(&s))) {
postfix[j++] = pop(&s);
}
push(&s, infix[i]);
}
}

while (!isEmpty(&s)) {
postfix[j++] = pop(&s);
}
postfix[j] = '\0';
}
int evaluatePostfix(char* postfix) {
Stack s;
int i;
initStack(&s);
for (i = 0; postfix[i]; i++) {
if (isOperand(postfix[i])) {
push(&s, postfix[i] - '0'); // Convert operand from char to int
} else {
int val1 = pop(&s);
int val2 = pop(&s);
switch (postfix[i]) {
case '+': push(&s, val2 + val1); break;
case '-': push(&s, val2 - val1); break;
case '*': push(&s, val2 * val1); break;
case '/': push(&s, val2 / val1); break;
}
}
}
return pop(&s);
}
int main() {
char infix[] = "3+(2*5)-9";
char postfix[MAX];
clrscr();
infixToPostfix(infix, postfix);
printf("Infix: %s\n", infix);
printf("Postfix: %s\n", postfix);
printf("Result: %d\n", evaluatePostfix(postfix));
getche();
return 0;
}
Output:
Infix: 3+(2*5)-9
Postfix: 325*+9-
Result: 4
Result:
Thus, a stack to evaluate an infix expression and convert it to postfix is executed
successfully.
ii. Aim: Create a program to determine whether a given string is a palindrome or not.
Description: A string is said to be palindrome if it remains the same on reading from both
ends. It means that when you reverse a given string, it should be the same as the original string.
For instance, the string ‘level’ is a palindrome because it remains the same when you read it
from the beginning to the end and vice versa.
Program:
#include <stdio.h>
#include <string.h>
#include <conio.h>
int isPalindrome(char str[]) {
int i = 0;
int j = strlen(str) - 1;
while (i < j) {
// Skip non-alphanumeric characters (optional)
while (!isalnum(str[i]) && i < j) i++;
while (!isalnum(str[j]) && i < j) j--;
// Check if the characters are different
if (tolower(str[i]) != tolower(str[j])) {
return 0; // Not a palindrome
}
i++;
j--;
}
return 1; // Is a palindrome
}
int main() {
char str[100];
clrscr();
printf("Enter a string: ");
fgets(str, sizeof(str), stdin); // Read string with spaces
str[strcspn(str, "\n")] = 0; // Remove trailing newline
if (isPalindrome(str)) {
printf("\"%s\" is a palindrome.\n", str);
} else {
printf("\"%s\" is not a palindrome.\n", str);
}
getche();
return 0;
}
Output:
Enter a string: Madam
“Madam” is a palindrome
Result:
Thus, a C program to determine whether a given string is a palindrome or notis executed
successfully.

iii. Aim: Implement a stack or queue to perform comparison and check for symmetry
Description:
A program to check if a given string or sequence is symmetric (a palindrome). This program
utilizes a stack data structure to push half of the sequence onto the stack and then compare it
with the second half of the sequence. If the stack contents match the corresponding characters
or elements of the sequence as they are popped, the sequence is symmetric. Otherwise, it is not.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define MAX 100

typedef struct {
int top;
char items[MAX];
} Stack;
void initStack(Stack *s) {
s->top = -1;
}
int isFull(Stack *s) {
return s->top == (MAX - 1);
}
int isEmpty(Stack *s) {
return s->top == -1;
}
void push(Stack *s, char value) {
if (isFull(s)) {
printf("Stack is full.\n");
} else {
s->items[++(s->top)] = value;
}
}
char pop(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty.\n");
return -1; // Return a value that indicates the stack is empty
} else {
return s->items[(s->top)--];
}
}
int checkSymmetry(char str[]) {
int length = strlen(str);
Stack s;
int i,start;
initStack(&s);
// Push first half of str onto stack
for (i = 0; i < length / 2; i++) {
push(&s, str[i]);
}
// Start comparison from the middle of the string
start = (length % 2 == 0) ? (length / 2) : (length / 2 + 1);
for (i = start; i < length; i++) {
if (str[i] != pop(&s)) {
return 0; // Not symmetric
}
}
return 1; // Symmetric
}
int main() {
char str[MAX];
clrscr();
printf("Enter a string: ");
fgets(str, MAX, stdin); // Reading string input, including spaces
str[strcspn(str, "\n")] = 0; // Removing the newline character
if (checkSymmetry(str)) {
printf("The string \"%s\" is symmetric.\n", str);
} else {
printf("The string \"%s\" is not symmetric.\n", str);
}
getche();
return 0;
}
Output:
Enter a string: madam
The string “madam” is symmetric.
Result: The program has been successfully executed

Experiment – 8
Hashing

i. Aim: Implement a hash table with collision resolution techniques.


Description: Hashing is a technique that is used to uniquely identify a specific object from a
group of similar objects.In hashing, large keys are converted into small keys by using hash
functions. The values are then stored in a data structure called hash table. The idea of hashing
is to distribute entries (key/value pairs) uniformly across an array. Each element is assigned a
key (converted key). By using that key you can access the element in O(1) time. Using the key,
the algorithm (hash function) computes an index that suggests where an entry can be found or
inserted.
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

#define INITIAL_TABLE_SIZE 10
#define LOAD_FACTOR_THRESHOLD 0.7

struct KeyValuePair {
char key[50];
char value[50];
int isOccupied; // flag to mark if entry is active, helps in deletion
};

struct HashTable {
int size;
int itemCount;
struct KeyValuePair* table;
};

struct KeyValuePair createKeyValuePair(const char* key, const char* value) {


struct KeyValuePair newPair;
strcpy(newPair.key, key);
strcpy(newPair.value, value);
newPair.isOccupied = 1;
return newPair;
}

unsigned long hashFunction(const char* str) {


unsigned long hash = 5381;
int c;
while ((c = *str++) != '\0')
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}

int linearProbe(int index, int attempt, int size) {


return (index + attempt) % size;
}

struct HashTable createHashTable(int size) {


struct HashTable newTable;
int i;
newTable.size = size;
newTable.itemCount = 0;
newTable.table = (struct KeyValuePair*)calloc(size, sizeof(struct KeyValuePair));
for (i = 0; i < size; i++)
newTable.table[i].isOccupied = 0; // Initialize occupancy flag
return newTable;
}

void resizeHashTable(struct HashTable* hashTable);

void insert(struct HashTable* hashTable, const char* key, const char* value) {
unsigned long index;
int attempt;
if (hashTable->itemCount >= LOAD_FACTOR_THRESHOLD * hashTable->size) {
resizeHashTable(hashTable);
}

index = hashFunction(key) % hashTable->size;


attempt = 0;
while (hashTable->table[index].isOccupied) {
attempt++;
index = linearProbe(index, attempt, hashTable->size);
}
hashTable->table[index] = createKeyValuePair(key, value);
hashTable->itemCount++;
}

void resizeHashTable(struct HashTable* hashTable) {


int i;
int newSize = 2 * hashTable->size;
struct HashTable newTable = createHashTable(newSize);
for (i = 0; i < hashTable->size; i++) {
if (hashTable->table[i].isOccupied) {
insert(&newTable, hashTable->table[i].key, hashTable->table[i].value);
}
}
free(hashTable->table);
*hashTable = newTable;
}

void displayHashTable(struct HashTable* hashTable) {


int i;
printf("Current Hash Table Contents:\n");
for (i = 0; i < hashTable->size; i++) {
if (hashTable->table[i].isOccupied) {
printf("[%d] -> Key: %s, Value: %s\n", i, hashTable->table[i].key, hashTable-
>table[i].value);
} else {
printf("[%d] -> Empty\n", i);
}
}
}
const char* retrieve(struct HashTable* hashTable, const char* key) {
unsigned long index = hashFunction(key) % hashTable->size;
int attempt = 0;
while (hashTable->table[index].isOccupied) {
if (strcmp(hashTable->table[index].key, key) == 0) {
return hashTable->table[index].value;
}
attempt++;
index = linearProbe(index, attempt, hashTable->size);
}
return "Key not found";
}
int main() {
struct HashTable hashTable = createHashTable(INITIAL_TABLE_SIZE);
clrscr();
insert(&hashTable, "Red", "#ff0000");
insert(&hashTable, "Green", "#008000");
insert(&hashTable, "Blue", "#0000FF");
insert(&hashTable, "Yellow", "#FFFF00");
insert(&hashTable, "Orange", "#FFA500");
printf("Hash Table:\n");
displayHashTable(&hashTable);
printf("\nValue for key 'Red': %s\n", retrieve(&hashTable, "Red"));
printf("Value for key 'Yellow': %s\n", retrieve(&hashTable, "Yellow"));
printf("Value for key 'White': %s\n", retrieve(&hashTable, "White"));
getch();
return 0;
}

Output:
Hash Table:
[0] -> (, )
[1] -> (Blue, #0000FF)
[2] -> (, )
[3] -> (, )
[4] -> (, )
[5] -> (, )
[6] -> (Green, #008000)
[7] -> (, )
[8] -> (Yellow, #FFFF00)
[9] -> (, )
[10] -> (Red, #ff0000)
[11] -> (, )
[12] -> (Orange, #FFA500)
[13] -> (, )
[14] -> (, )
[15] -> (, )
[16] -> (, )
[17] -> (, )
[18] -> (, )
[19] -> (, )

Value for key 'Red': #ff0000


Value for key 'Yellow': #FFFF00
Value for key 'White': Key not found
Result:
Thus, a hash table with collision resolution techniques is implemented successfully.

ii. Aim: To Write a program to implement a simple cache using hashing.


Description: Cache replacement algorithms are efficiently designed to replace the cache when
the space is full. The Least Recently Used (LRU) is one of those algorithms. As the name
suggests when the cache memory is full, LRU picks the data that is least recently used and
removes it in order to make space for the new data. The priority of the data in the cache changes
according to the need of that data i.e. if some data is fetched or updated recently then the priority
of that data would be changed and assigned to the highest priority, and the priority of the data
decreases if it remains unused operations after operations.
Program:
#include <stdio.h>
#include <stdlib.h>

typedef struct cacheNode {


int key;
int data;
struct cacheNode* next;
} CacheNode;

typedef struct {
CacheNode** buckets;
int capacity;
} Cache;
// Hash function
unsigned int hash(int key, int capacity) {
return (unsigned int)key % capacity;
}

// Create a new cache node


CacheNode* createNode(int key, int data) {
CacheNode* newNode = (CacheNode*)malloc(sizeof(CacheNode));
newNode->key = key;
newNode->data = data;
newNode->next = NULL;
return newNode;
}

// Initialize the cache


void initCache(Cache* cache, int capacity) {
int i;
cache->capacity = capacity;
cache->buckets = (CacheNode**)malloc(capacity * sizeof(CacheNode*));
for (i = 0; i < capacity; i++) {
cache->buckets[i] = NULL;
}
}

// Insert data into the cache


void cacheInsert(Cache* cache, int key, int data) {
unsigned int index = hash(key, cache->capacity);
CacheNode* newNode = createNode(key, data);
// Insert the new node at the beginning of the chain (for simplicity)
newNode->next = cache->buckets[index];
cache->buckets[index] = newNode;
printf("Cached: Key = %d, Data = %d\n", key, data);
}

// Search for a key in the cache


int cacheSearch(Cache* cache, int key) {
unsigned int index = hash(key, cache->capacity);
CacheNode* temp = cache->buckets[index];
while (temp != NULL) {
if (temp->key == key) {
return temp->data;
}
temp = temp->next;
}
return -1; // Key not found
}

// Display the cache contents


void displayCache(Cache* cache) {
int i;
printf("Cache Contents:\n");
for (i = 0; i < cache->capacity; i++) {
CacheNode* temp = cache->buckets[i];
if (temp != NULL) {
printf("Bucket[%d]: ", i);
while (temp != NULL) {
printf("(%d, %d) ", temp->key, temp->data);
temp = temp->next;
}
printf("\n");
}
}
}

// Main function to demonstrate the cache operations


int main() {
Cache myCache;
int key,data;
clrscr();
initCache(&myCache, 10); // Initialize the cache with 10 buckets
// Insert some data into the cache
cacheInsert(&myCache, 1, 100);
cacheInsert(&myCache, 2, 200);
cacheInsert(&myCache, 11, 1100); // This will cause a collision with key
// Display the cache
displayCache(&myCache);
// Search for data in the cache
key = 11;
data = cacheSearch(&myCache, key);
if (data != -1) {
printf("Data for key %d: %d\n", key, data);
} else {
printf("Key %d not found in cache.\n", key);
}
getche();
return 0;
}
Output:
Cached: Key = 1, Data =100
Cached: Key = 2, Data =200
Cached: Key = 11, Data =1100
Cache Contents:
Bucket[1]: (11,1100) (1,100)
Bucket[2]: (2,200)
Data for key 11: 1100
Result:
Hence, a C program to implement a simple cache using hashing is successfully
executed.
Experiment – 9
Binary Search Tree

i. Aim: To Implement a BST using Linked List and a traversing of BST.


Description: A binary Search Tree is a binary tree where the value of any node is greater than
the left subtree and less than the right subtree.
Properties of Binary Search Tree
Following are some main properties of the binary search tree in C:
• All nodes of the left subtree are less than the root node and nodes of the right subtree
are greater than the root node.
• The In-order traversal of binary search trees gives the values in ascending order.
• All the subtrees of BST hold the same properties

Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

typedef struct node {


int key;
struct node *left, *right;
} Node;

// Function to create a new node


Node* createNode(int key) {
Node *newNode = (Node*) malloc(sizeof(Node));
if (newNode == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
newNode->key = key;
newNode->left = newNode->right = NULL;
return newNode;
}
// Function to insert a new node with given key in BST
Node* insert(Node* node, int key) {
// If the tree is empty, return a new node
if (node == NULL) {
return createNode(key);
}

// Otherwise, recur down the tree


if (key < node->key) {
node->left = insert(node->left, key);
} else if (key > node->key) {
node->right = insert(node->right, key);
}

// return the (unchanged) node pointer


return node;
}

// Function to do inorder traversal of BST


void inorderTraversal(Node* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->key);
inorderTraversal(root->right);
}
}

// Main function to demonstrate the operations


int main() {
Node *root = NULL;
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);

printf("Inorder traversal of the binary search tree: ");


inorderTraversal(root);
printf("\n");
getch();
return 0;
}
Output:
Inorder traversal of the binary search tree: 20 30 40 50 60 70 80
Result:
Hence, a BST using Linked List is successfully implemented.

ii. Aim: To Implement traversing of BST.


Description: A binary Search Tree is a binary tree where the value of any node is greater than
the left subtree and less than the right subtree.
Properties of Binary Search Tree
Following are some main properties of the binary search tree in C:
• All nodes of the left subtree are less than the root node and nodes of the right subtree
are greater than the root node.
• The In-order traversal of binary search trees gives the values in ascending order.
• All the subtrees of BST hold the same properties
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

typedef struct node {


int key;
struct node *left, *right;
} Node;

// Function to create a new node


Node* newNode(int item) {
Node *temp = (Node *)malloc(sizeof(Node));
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}

// Function to insert a new node with a given key in the BST


Node* insert(Node* node, int key) {
// If the tree is empty, return a new node
if (node == NULL) return newNode(key);

// Otherwise, recur down the tree


if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
// return the (unchanged) node pointer
return node;
}

// In-order traversal of the BST


void inorder(Node *root) {
if (root != NULL) {
inorder(root->left);
printf("%d ", root->key);
inorder(root->right);
}
}

// Pre-order traversal of the BST


void preorder(Node *root) {
if (root != NULL) {
printf("%d ", root->key);
preorder(root->left);
preorder(root->right);
}
}

// Post-order traversal of the BST


void postorder(Node *root) {
if (root != NULL) {
postorder(root->left);
postorder(root->right);
printf("%d ", root->key);
}
}

// Main function to demonstrate tree traversals


int main() {
Node *root = NULL;
clrscr();
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);
printf("In-order traversal of the binary search tree: ");
inorder(root);
printf("\n");

printf("Pre-order traversal of the binary search tree: ");


preorder(root);
printf("\n");

printf("Post-order traversal of the binary search tree: ");


postorder(root);
printf("\n");
getch();
return 0;
}
Output:
In-order traversal of the binary search tree: 20 30 40 50 60 70 80
Pre-order traversal of the binary search tree: 50 30 20 40 70 60 80
Post-order traversal of the binary search tree: 20 40 30 60 80 70 50
Result:
Traversing of a linked list is done successfully

Experiment – 10
Graphs

i. Aim: To Implement a DFS Traversal of a graph.


Description: Depth First Traversal (or DFS) for a graph is similar to Depth First Traversal
of a tree. The only catch here is, that, unlike trees, graphs may contain cycles (a node may be
visited twice). To avoid processing a node more than once, use a boolean visited array. A graph
can have more than one DFS traversal. Depth-first search is an algorithm for traversing or
searching tree or graph data structures. The algorithm starts at the root node (selecting some
arbitrary node as the root node in the case of a graph) and explores as far as possible along each
branch before backtracking.
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

struct node {
int vertex;
struct node* next;
};

struct node* createNode(int v) {


struct node* newNode = malloc(sizeof(struct node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}

struct Graph {
int totalVertices;
int* visited;
struct node** adjLists;
};

struct Graph* createGraph(int vertices) {


struct Graph* graph = malloc(sizeof(struct Graph));
int i;
graph->totalVertices = vertices;
graph->adjLists = malloc(vertices * sizeof(struct node*));
graph->visited = malloc(vertices * sizeof(int));
for (i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
graph->visited[i] = 0;
}
return graph;
}

void addEdge(struct Graph* graph, int src, int dest) {


struct node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
newNode = createNode(src);
newNode->next = graph->adjLists[dest];
graph->adjLists[dest] = newNode;
}

void displayGraph(struct Graph* graph) {


int v;
for (v = 0; v < graph->totalVertices; v++) {
struct node* temp = graph->adjLists[v];
printf("\n%d => ", v);
while (temp) {
printf("%d, ", temp->vertex);
temp = temp->next;
}
printf("\n");
}
printf("\n");
}

void DFS(struct Graph* graph, int vertex) {


struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
graph->visited[vertex] = 1;
printf("%d -> ", vertex);
while (temp != NULL) {
int connectedVertex = temp->vertex;
if (graph->visited[connectedVertex] == 0) {
DFS(graph, connectedVertex);
}
temp = temp->next;
}
}

int main() {
int numVertices = 8; // Assuming vertices start from 0 to 7
struct Graph* graph = createGraph(numVertices);
clrscr();
addEdge(graph, 0, 4);
addEdge(graph, 0, 1);
addEdge(graph, 0, 2);
addEdge(graph, 2, 5);
addEdge(graph, 1, 6);
addEdge(graph, 1, 3);
printf("\nThe Adjacency List of the Graph is:");
displayGraph(graph);
printf("\nDFS traversal of the graph starting from vertex 0:\n");
DFS(graph, 0);
printf("\n");
getch();
return 0;
}
Output:
The Adjacency List of the Graph is:
0 => 2, 1, 4,

1 => 3, 6, 0,

2 => 5, 0,

3 => 1,
4 => 0,

5 => 2,

6 => 1,

7 =>

DFS traversal of the graph starting from vertex 0:


0 -> 2 -> 5 -> 1 -> 3 -> 6 -> 4 ->
Result:
Hence, a DFS Traversal of a graph is implemented successfully.

ii. Aim:Write a program to implement a BFS Traversal of a graph


Description:
Breadth First Search (BFS) is a graph traversal algorithm that explores all the vertices in a
graph at the current depth before moving on to the vertices at the next depth level. It starts at a
specified vertex and visits all its neighbors before moving on to the next level of
neighbors. BFS is commonly used in algorithms for pathfinding, connected components, and
shortest path problems in graphs. The only catch here is, that, unlike trees, graphs may contain
cycles, so we may come to the same node again. To avoid processing a node more than once,
we divide the vertices into two categories:
• Visited and
• Not visited.
A boolean visited array is used to mark the visited vertices. For simplicity, it is assumed that
all vertices are reachable from the starting vertex. BFS uses a queue data structure for traversal.
Program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define MAX_VERTICES 100

void addEdge(int graph[MAX_VERTICES][MAX_VERTICES], int start, int end) {


graph[start][end] = 1;
graph[end][start] = 1; // For undirected graph
}

void BFS(int graph[MAX_VERTICES][MAX_VERTICES], int vertices, int startVertex) {


int visited[MAX_VERTICES] = {0}; // Initialize all vertices as not visited
int queue[MAX_VERTICES];
int front = 0, rear = 0; // Initialize the queue pointers correctly
visited[startVertex] = 1;
queue[rear] = startVertex; // Add the start vertex to the queue
printf("BFS Traversal Order: ");
while (front <= rear) { // Ensure traversal while there are elements in the queue
int currentVertex = queue[front++];
int i;
printf("%d ", currentVertex);
for (i = 0; i < vertices; i++) {
if (graph[currentVertex][i] == 1 && !visited[i]) {
visited[i] = 1;
queue[++rear] = i; // Add vertex to the queue
}
}
}
printf("\n");
}

int main() {
int vertices, edges,i,startVertex;
int graph[MAX_VERTICES][MAX_VERTICES];
clrscr();
printf("Input the number of vertices: ");
scanf("%d", &vertices);
if (vertices <= 0 || vertices > MAX_VERTICES) {
printf("Invalid number of vertices. Exiting...\n");
return 1;
}
graph[MAX_VERTICES][MAX_VERTICES] = 0; // Initialize the adjacency matrix with
zeros
printf("Input the number of edges: ");
scanf("%d", &edges);
if (edges < 0 || edges > vertices * (vertices - 1) / 2) {
printf("Invalid number of edges. Exiting...\n");
return 1;
}
for (i = 0; i < edges; i++) {
int start, end;
printf("Input edge %d (start end): ", i + 1);
scanf("%d %d", &start, &end);
if (start < 0 || start >= vertices || end < 0 || end >= vertices) {
printf("Invalid vertices. Try again.\n");
i--;
continue;
}
addEdge(graph, start, end);
}
printf("Input the starting vertex for BFS traversal: ");
scanf("%d", &startVertex);
if (startVertex < 0 || startVertex >= vertices) {
printf("Invalid start vertex. Exiting...\n");
return 1;
}
BFS(graph, vertices, startVertex);
getch();
return 0;
}
Output:
Input the number of vertices: 5
Input the number of edges: 4
Input edge 1 (start end): 0 1
Input edge 2 (start end): 0 2
Input edge 3 (start end): 1 3
Input edge 4 (start end): 1 4
Input the starting vertex for BFS traversal: 0
BFS Traversal Order: 0 1 2 3 4

Result:
Hence, a BFS Traversal of a graph is implemented successfully.

You might also like