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

Lab File - DAA

The document outlines a course on Design and Analysis of Algorithms at Galgotias University, detailing various experiments to implement and analyze different algorithms including search and sorting techniques. It includes specific aims for each experiment, such as implementing linear and binary search algorithms, and various sorting algorithms like selection, insertion, bubble, merge, heap, and quick sort, along with their time complexities. The document also provides sample code and results from experiments conducted on varying sizes of integer arrays.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views49 pages

Lab File - DAA

The document outlines a course on Design and Analysis of Algorithms at Galgotias University, detailing various experiments to implement and analyze different algorithms including search and sorting techniques. It includes specific aims for each experiment, such as implementing linear and binary search algorithms, and various sorting algorithms like selection, insertion, bubble, merge, heap, and quick sort, along with their time complexities. The document also provides sample code and results from experiments conducted on varying sizes of integer arrays.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 49

Galgotias University

Plot No.2, Sector-17A, Yamuna Expressway,


Greater Noida, Gautam Buddh Nagar, U.P., India

Course Name: Design and Analysis of Algorithm


School: SCAT
Year: 2nd Semester: III
Program: MCA Session: 2023-25

Submitted By: Submitted To:

Name: Prabhat Kumar Dr. Sudeshna Chakraborty


Admission No: [DAA Professor, SCAT]
23SCSE2030611
Page |2

Sr. Name of Experiment Page Date of Experiment Signature of Faculty


No No.
.
To implement Linear
and Binary search
algorithms and compute
1. 3 09/07/24
their running time for
worst, best and average
cases.
To sort a given set of n
integer elements using
the following iterative
sorting algorithms and 16/07/2024
compute their time
complexity for worst
case, average case and 23/07/2024
2. best case. Run the 7
program for varied
values of n> 5000 and
record the time taken to
sort.
● Selection Sort
● Insertion Sort
● Bubble Sort
To sort a given set of n
integer elements using
divide-and-conquer
based following sorting 30/07/2024
algorithms and compute
their time complexity
for worst case, average 06/08/2024
3. case and best case. Run 11
the program for varied
values of n> 10000 and
record the time taken to
sort:
● Merge Sort
● Heap Sort
● Quick Sort

Index
Page |3

Sr.
Page
No Name of Experiment Date of Experiment Signature of Faculty
No.
.
To short a given array
using: 23/08/24
4.  Count Sort 18
 Radix Sort 30/08/24
 Bucket Sort

To implement Binary
06/09/24
5. Tree Algorithm on the 23
given set of elements.

To implement balanced
06/09/24
Binary Search Tree
6. 26
Algorithm on the given
set of elements.

To implement Red Black


7. Tree algorithm on the 31 13/09/24
given set of elements.

To implement BTree
8. Algorithm on the given 36 13/09/24
set of elements.

To implement Greedy
Algorithm Approach on
9. 40 21/09/24
Minimum Spanning
Tree (MST).

To implement Knap
Sack Problem using
10. 44 21/09/24
Greedy Algorithm
approach.

To implement Travelling
11 Salesman Problem using 46 28/09/24
Greedy Approach.

To implement Coin
Sequence Problem of
12 finding minimum 48 28/09/24
number of coins through
Greedy Approach.
Page |4

Experiment no. 1
Aim: To implement Linear and Binary search algorithms and compute their running
time for worst, best and average cases.

Linear Search Algorithm:


The linear search algorithm is defined as a sequential search algorithm that starts at one
end and goes through each element of a list until the desired element is found;
otherwise, the search continues till the end of the dataset. In this article, we will learn
about the basics of the linear search algorithm, its applications, advantages,
disadvantages, and more to provide a deep understanding of linear search.

#include <stdio.h>

int search(int arr[], int N, int x)


{
for (int i = 0; i < N; i++)
if (arr[i] == x)
return i;
return -1;
}

// Driver code
int main(void)
{
int arr[] = { 2, 3, 4, 10, 40 };
int x = 10;
int N = sizeof(arr) / sizeof(arr[0]);

// Function call
int result = search(arr, N, x);
(result == -1)
? printf("Element is not present in array")
: printf("Element is present at index %d", result);
return 0;
}
Page |5

Output:

Output:

Time Complexity:

Best Case: In the best case, the key might be present at the first index. So the best case
complexity is O(1)
Worst Case: In the worst case, the key might be present at the last index i.e., opposite to
the end from which the search has started in the list. So the worst-case complexity is
O(N) where N is the size of the list.
Average Case: O(N)

Binary Search Algorithm:

Binary Search Algorithm is a searching algorithm used in a sorted array by repeatedly


dividing the search interval in half. The idea of binary search is to use the information
that the array is sorted and reduce the time complexity to O(log N).

// C program to implement iterative Binary Search


#include <stdio.h>

// An iterative binary search function.


int binarySearch(int arr[], int low, int high, int x)
{
while (low <= high) {
int mid = low + (high - low) / 2;

// Check if x is present at mid


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

// If x greater, ignore left half


if (arr[mid] < x)
Page |6

low = mid + 1;

// If x is smaller, ignore right half


else
high = mid - 1;
}

// If we reach here, then element was not present


return -1;
}

// Driver code
int main(void){
int arr[] = { 2, 3, 4, 10, 40 };
int n = sizeof(arr) / sizeof(arr[0]);
int x = 10;
int result = binarySearch(arr, 0, n - 1, x);
if(result == -1){
printf("Element is not present in array");
}
else
printf("Element is present at index %d",result);

Using Recursive approach:

// C program to implement recursive Binary Search


#include <stdio.h>

// A recursive binary search function. It returns


// location of x in given array arr[low..high] is present,
// otherwise -1

int binarySearch(int arr[], int low, int high, int x)


{
if (high >= low) {
int mid = low + (high - low) / 2;

// If the element is present at the middle


// itself
if (arr[mid] == x)
return mid;

// If element is smaller than mid, then


// it can only be present in left subarray
if (arr[mid] > x)
return binarySearch(arr, low, mid - 1, x);

// Else the element can only be present


Page |7

// in right subarray
return binarySearch(arr, mid + 1, high, x);
}

// We reach here when element is not


// present in array
return -1;
}

// Driver code

int main()
{
int arr[] = { 2, 3, 4, 10, 40 };
int n = sizeof(arr) / sizeof(arr[0]);
int x = 10;
int result = binarySearch(arr, 0, n - 1, x);
if (result == -1) printf("Element is not present in array");
else printf("Element is present at index %d", result);
return 0;
}

Time Complexity:
 Best Case: O(1)
 Average Case: O(log N)
 Worst Case: O(log N)

Output:
Page |8

Experiment no. 2

Aim: To sort a given set of n integer elements using the following iterative sorting
algorithms and compute their time complexity for worst case, average case and best
case. Run the program for varied values of n> 5000 and record the time taken to sort.
● Selection Sort
● Insertion Sort
● Bubble Sort

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Function to perform Selection Sort


void selectionSort(int arr[], int n) {
int i, j, minIdx, temp;
for (i = 0; i < n-1; i++) {
minIdx = i;
for (j = i+1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
// Swap the found minimum element with the first element
temp = arr[minIdx];
arr[minIdx] = arr[i];
arr[i] = temp;
}
}

// Function to perform Insertion Sort


void insertionSort(int arr[], int n) {
int i, key, j;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
// Move elements of arr[0..i-1] that are greater than key
// to one position ahead of their current position
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}

// Function to perform Bubble Sort


Page |9

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


int i, j, temp;
for (i = 0; i < n-1; i++) {
for (j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
// Swap arr[j] and arr[j+1]
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}

// Function to generate an array of random integers


void generateArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
arr[i] = rand() % 10000; // Generate random numbers between 0 and 9999
}
}

// Function to copy an array


void copyArray(int source[], int dest[], int n) {
for (int i = 0; i < n; i++) {
dest[i] = source[i];
}
}

// Function to measure and print time taken for sorting


void measureSortTime(void (*sortFunc)(int[], int), int arr[], int n, const char *sortName) {
int *arrCopy = (int *)malloc(n * sizeof(int));
copyArray(arr, arrCopy, n);

clock_t start = clock();


sortFunc(arrCopy, n);
clock_t end = clock();

double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;


printf("%s took %f seconds to sort %d elements.\n", sortName, time_taken, n);

free(arrCopy);
}

int main() {
srand(time(NULL));

int n[] = {5000, 10000, 15000, 20000}; // Varied values of n


int num_tests = sizeof(n)/sizeof(n[0]);
P a g e | 10

for (int t = 0; t < num_tests; t++) {


int size = n[t];
int *arr = (int *)malloc(size * sizeof(int));

// Generate random array


generateArray(arr, size);

printf("\nTesting with n = %d\n", size);

// Measure time for Selection Sort


measureSortTime(selectionSort, arr, size, "Selection Sort");

// Measure time for Insertion Sort


measureSortTime(insertionSort, arr, size, "Insertion Sort");

// Measure time for Bubble Sort


measureSortTime(bubbleSort, arr, size, "Bubble Sort");

free(arr);
}

return 0;
}

Time Complexity Analysis of Selection Sort:

Best-case: O(n2), best case occurs when the array is already sorted. (where n is the
number of integers in an array)

Average-case: O(n2), the average case arises when the elements of the array are in a
disordered or random order, without a clear ascending or descending pattern.

Worst-case: O(n2), The worst-case scenario arises when we need to sort an array in
ascending order, but the array is initially in descending order.

Time Complexity of Insertion Sort:

Best case: O(n) , If the list is already sorted, where n is the number of elements in the
list.
Average case: O(n2 ) , If the list is randomly ordered
Worst case: O(n2 ) , If the list is in reverse order

Time Complexity of Bubble Sort:

Best Case Time Complexity Analysis of Bubble Sort: O(N)

Worst Case Time Complexity Analysis of Bubble Sort: O(N2)

Average Case Time Complexity Analysis of Bubble Sort: O(N2)


P a g e | 11

Output:

Testing with n = 5000


Selection Sort took 0.044000 seconds to sort 5000 elements.
Insertion Sort took 0.022000 seconds to sort 5000 elements.
Bubble Sort took 0.078000 seconds to sort 5000 elements.

Testing with n = 10000


Selection Sort took 0.171000 seconds to sort 10000 elements.
Insertion Sort took 0.100000 seconds to sort 10000 elements.
Bubble Sort took 0.368000 seconds to sort 10000 elements.

Testing with n = 15000


Selection Sort took 0.380000 seconds to sort 15000 elements.
Insertion Sort took 0.217000 seconds to sort 15000 elements.
Bubble Sort took 0.991000 seconds to sort 15000 elements.

Testing with n = 20000


Selection Sort took 0.684000 seconds to sort 20000 elements.
Insertion Sort took 0.373000 seconds to sort 20000 elements.
Bubble Sort took 1.589000 seconds to sort 20000 elements.
P a g e | 12

Experiment no. 3

Aim: To sort a given set of n integer elements using divide-and-conquer based


following sorting algorithms and compute their time complexity for worst case,
average case and best case. Run the program for varied values of n> 10000 and record
the time taken to sort:
● Merge Sort
● Heap Sort
● Quick Sort

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Function to merge two subarrays in Merge Sort


void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;

// Create temp arrays


int L[n1], R[n2];

// Copy data to temp arrays


for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];

// Merge the temp arrays back into arr[l..r]


i = 0; // Initial index of first subarray
j = 0; // Initial index of second subarray
k = l; // Initial index of merged subarray
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}

// Copy the remaining elements of L[], if any


while (i < n1) {
arr[k] = L[i];
P a g e | 13

i++;
k++;
}

// Copy the remaining elements of R[], if any


while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}

// Function to perform Merge Sort


void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;

// Sort first and second halves


mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);

// Merge the sorted halves


merge(arr, l, m, r);
}
}

// Function to heapify a subtree rooted with node i in Heap Sort


void heapify(int arr[], int n, int i) {
int largest = i; // Initialize largest as root
int left = 2 * i + 1; // left = 2*i + 1
int right = 2 * i + 2; // right = 2*i + 2

// If left child is larger than root


if (left < n && arr[left] > arr[largest])
largest = left;

// If right child is larger than largest so far


if (right < n && arr[right] > arr[largest])
largest = right;

// If largest is not root


if (largest != i) {
int temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;

// Recursively heapify the affected sub-tree


heapify(arr, n, largest);
}
P a g e | 14

// Function to perform Heap Sort


void heapSort(int arr[], int n) {
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);

// One by one extract an element from heap


for (int i = n - 1; i > 0; i--) {
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;

// Call max heapify on the reduced heap


heapify(arr, i, 0);
}
}

// Function to partition the array in Quick Sort


int partition(int arr[], int low, int high) {
int pivot = arr[high]; // pivot
int i = (low - 1); // Index of smaller element

for (int j = low; j <= high - 1; j++) {


// If current element is smaller than the pivot
if (arr[j] < pivot) {
i++; // increment index of smaller element
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);
}

// Function to perform Quick Sort


void quickSort(int arr[], int low, int high) {
if (low < high) {
// pi is partitioning index, arr[p] is now at right place
int pi = partition(arr, low, high);

// Separately sort elements before partition and after partition


quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
P a g e | 15

}
}

// Function to generate an array of random integers


void generateArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
arr[i] = rand() % 10000; // Generate random numbers between 0 and 9999
}
}

// Function to copy an array


void copyArray(int source[], int dest[], int n) {
for (int i = 0; i < n; i++) {
dest[i] = source[i];
}
}

// Function to measure and print time taken for sorting


void measureSortTime(void (*sortFunc)(int[], int), int arr[], int n, const char *sortName) {
int *arrCopy = (int *)malloc(n * sizeof(int));
copyArray(arr, arrCopy, n);

clock_t start = clock();


sortFunc(arrCopy, n);
clock_t end = clock();

double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;


printf("%s took %f seconds to sort %d elements.\n", sortName, time_taken, n);

free(arrCopy);
}

// Wrapper function to handle Quick Sort measurement


void measureQuickSortTime(int arr[], int n, const char *sortName) {
int *arrCopy = (int *)malloc(n * sizeof(int));
copyArray(arr, arrCopy, n);

clock_t start = clock();


quickSort(arrCopy, 0, n - 1);
clock_t end = clock();

double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;


printf("%s took %f seconds to sort %d elements.\n", sortName, time_taken, n);

free(arrCopy);
}

int main() {
srand(time(NULL));
P a g e | 16

int n[] = {10000, 20000, 30000, 40000}; // Varied values of n


int num_tests = sizeof(n)/sizeof(n[0]);

for (int t = 0; t < num_tests; t++) {


int size = n[t];
int *arr = (int *)malloc(size * sizeof(int));

// Generate random array


generateArray(arr, size);

printf("\nTesting with n = %d\n", size);

// Measure time for Merge Sort


measureSortTime(mergeSort, arr, size, "Merge Sort");

// Measure time for Heap Sort


measureSortTime(heapSort, arr, size, "Heap Sort");

// Measure time for Quick Sort


measureQuickSortTime(arr, size, "Quick Sort");

free(arr);
}

return 0;
}

Time Complexity of Merge Sort:

Best Case Time Complexity of Merge Sort: O(n∗logn)


Average Case Time Complexity of Merge Sort: O(n∗logn)
Worst Case Time Complexity of Merge Sort: O(n∗logn)

Time Complexity of Heap Sort:

The time complexity of Heap Sort in both best case and worst case scenarios is O(n log
n).

Time Complexity of Quick Sort:

The time complexity of quick sort is:


Best case: O(n log n)
Average case: O(n log n)
Worst case: O(n^2)
P a g e | 17

Output:

Testing with n = 10000


Merge Sort took 0.000000 seconds to sort 10000 elements.
Heap Sort took 0.003000 seconds to sort 10000 elements.
Quick Sort took 0.002000 seconds to sort 10000 elements.

Testing with n = 20000


Merge Sort took 0.000000 seconds to sort 20000 elements.
Heap Sort took 0.006000 seconds to sort 20000 elements.
Quick Sort took 0.004000 seconds to sort 20000 elements.

Testing with n = 30000


Merge Sort took 0.000000 seconds to sort 30000 elements.
Heap Sort took 0.012000 seconds to sort 30000 elements.
Quick Sort took 0.006000 seconds to sort 30000 elements.

Testing with n = 40000


Merge Sort took 0.000000 seconds to sort 40000 elements.
Heap Sort took 0.012000 seconds to sort 40000 elements.
Quick Sort took 0.008000 seconds to sort 40000 elements.
P a g e | 18

Experiment no. 4
Aim: To short a given array using:
 Count Sort
 Radix Sort
 Bucket Sort

Count Sort:

#include<stdio.h>
#include<limits.h>
#include<stdlib.h>

int maximum(int A[], int n){


int max = INT_MIN;
for(int i=0; i<=n; i++){
if(max < A[i]){
max = A[i];
}
}
return max;

void CountSort(int* A, int n){


int max = maximum(A, n);
int i, j;
int* count = (int*) malloc((max+1)*sizeof(int));

for(i = 0; i<=max; i++){


count[i] = 0;
}

for(i = 0; i<n; i++){


count[A[i]] = count[A[i]]+1;
}

i=0;
j=0;

while(i<=max){
if(count[i]>0){
A[j] = i;
count[i] = count[i] - 1;
j++;
}
else{
i++;
}
P a g e | 19

void printArray(int *A, int n){


for(int i = 0; i<=n-1; i++){
printf("%d ", A[i]);
}
}

int main(){
int A[] = {23,78,45,8,32,56};
printf("Original Array: \n");
int n = 6;
printArray(A, n);

printf("\nSorted Array: \n");


CountSort(A, n);
printArray(A, n);

return 0;

Output:
P a g e | 20

Radix Sort:

#include <stdio.h>

int getMax(int arr[], int n) {


int mx = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > mx)
mx = arr[i];
return mx;
}

void countSort(int arr[], int n, int exp) {


int output[n];
int count[10] = {0};

for (int i = 0; i < n; i++)


count[(arr[i] / exp) % 10]++;

for (int i = 1; i < 10; i++)


count[i] += count[i - 1];

for (int i = n - 1; i >= 0; i--) {


output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}

for (int i = 0; i < n; i++)


arr[i] = output[i];
}

void radixSort(int arr[], int n) {

// Find the maximum number to know


// the number of digits
int m = getMax(arr, n);

// Do counting sort for every digit


// exp is 10^i where i is the current
// digit number
for (int exp = 1; m / exp > 0; exp *= 10)
countSort(arr, n, exp);
}
P a g e | 21

// A utility function to print an array


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

// Driver code
int main() {
int arr[] = {2546, 124, 6, 1230, 431, 126};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original Array: \n");
printArray(arr, n);

// Function call
radixSort(arr, n);
printf("Sorted Array: \n");
printArray(arr, n);
return 0;
}

Output:
P a g e | 22

Bucket Sort:

#include <stdio.h>
void bucketsort(int a[], int n){ // function to implement bucket sort
int max = a[0]; // get the maximum element in the array
for (int i = 1; i < n; i++)
if (a[i] > max)
max = a[i];
int b[max], i;
for (int i = 0; i <= max; i++) {
b[i] = 0;
}
for (int i = 0; i < n; i++) {
b[a[i]]++;
}
for (int i = 0, j = 0; i <= max; i++) {
while (b[i] > 0) {
a[j++] = i;
b[i]--;
}
}
}
int main(){
int a[] = {12, 45, 33, 87, 56, 9, 11, 7, 67};
int n = sizeof(a) / sizeof(a[0]); // n is the size of array
printf("Before sorting array elements are: \n");
for (int i = 0; i < n; ++i)
printf("%d ", a[i]);
bucketsort(a, n);
printf("\nAfter sorting array elements are: \n");
for (int i = 0; i < n; ++i)
printf("%d ", a[i]);
}

Output:
P a g e | 23

Experiment no. 5

Aim: To implement Binary Tree Algorithm on the given set of elements.

A Binary Tree Data Structure is a hierarchical data structure in which each node has at
most two children, referred to as the left child and the right child. It is commonly used in
computer science for efficient storage and retrieval of data, with various operations such
as insertion, deletion, and traversal.

Tree Traversal refers to the process of visiting or accessing each node of the tree exactly
once in a certain order. Tree traversal algorithms help us to visit and process all the
nodes of the tree. Since tree is not a linear data structure, there are multiple nodes
which we can visit after visiting a certain node. There are multiple tree traversal
techniques which decide the order in which the nodes of the tree are to be visited.

A Tree Data Structure can be traversed in following ways:

Depth First Search or DFS


 Inorder Traversal
 Preorder Traversal
 Postorder Traversal

Traversal:

// Tree traversal in C

#include <stdio.h>
#include <stdlib.h>

struct node {
int item;
struct node* left;
struct node* right;
};

// Inorder traversal
void inorderTraversal(struct node* root) {
if (root == NULL) return;
inorderTraversal(root->left);
printf("%d ->", root->item);
inorderTraversal(root->right);
}

// Preorder traversal
void preorderTraversal(struct node* root) {
if (root == NULL) return;
printf("%d ->", root->item);
preorderTraversal(root->left);
P a g e | 24

preorderTraversal(root->right);
}

// Postorder traversal
void postorderTraversal(struct node* root) {
if (root == NULL) return;
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ->", root->item);
}

// Create a new Node


struct node* createNode(value) {
struct node* newNode = malloc(sizeof(struct node));
newNode->item = value;
newNode->left = NULL;
newNode->right = NULL;

return newNode;
}

// Insert on the left of the node


struct node* insertLeft(struct node* root, int value) {
root->left = createNode(value);
return root->left;
}

// Insert on the right of the node


struct node* insertRight(struct node* root, int value) {
root->right = createNode(value);
return root->right;
}

int main() {
struct node* root = createNode(1);
insertLeft(root, 2);
insertRight(root, 3);
insertLeft(root->left, 4);

printf("Inorder traversal \n");


inorderTraversal(root);

printf("\nPreorder traversal \n");


preorderTraversal(root);

printf("\nPostorder traversal \n");


postorderTraversal(root);
}
P a g e | 25

The time complexity of binary tree traversal depends on the type of traversal and the
structure of the tree:

In-order traversal in a threaded binary tree:


This traversal is fast because the next node can be found in O(1) time.

Breadth-First Search (BFS) and Depth-First Search (DFS) traversal:


The time complexity for both of these algorithms is O(n) because each node is visited
exactly once.

Postorder traversal:
The time complexity for this traversal is O(n), where n is the size of the binary tree.

Searching an element in a balanced binary search tree (BST):


The time complexity for this is O(log(n)), where n is the number of nodes in the tree. This
is because the search space is halved at each level.

AVL tree traversal:


The worst case time complexity for this is O(logN) because the tree's height is
guaranteed to be O(logN).

Output:
P a g e | 26

Experiment no. 6
Aim: To implement balanced Binary Search Tree Algorithm on the given set of
elements.

// C program to insert a node in AVL tree


#include<stdio.h>
#include<stdlib.h>

// An AVL tree node


struct Node
{
int key;
struct Node *left;
struct Node *right;
int height;
};

// A utility function to get the height of the tree


int height(struct Node *N)
{
if (N == NULL)
return 0;
return N->height;
}

// A utility function to get maximum of two integers


int max(int a, int b)
{
return (a > b)? a : b;
}

/* Helper function that allocates a new node with the given key and
NULL left and right pointers. */
struct Node* newNode(int key)
{
struct Node* node = (struct Node*)
malloc(sizeof(struct Node));
node->key = key;
node->left = NULL;
node->right = NULL;
node->height = 1; // new node is initially added at leaf
return(node);
}

// A utility function to right rotate subtree rooted with y


// See the diagram given above.
struct Node *rightRotate(struct Node *y)
{
P a g e | 27

struct Node *x = y->left;


struct Node *T2 = x->right;

// Perform rotation
x->right = y;
y->left = T2;

// Update heights
y->height = max(height(y->left),
height(y->right)) + 1;
x->height = max(height(x->left),
height(x->right)) + 1;

// Return new root


return x;
}

// A utility function to left rotate subtree rooted with x


// See the diagram given above.
struct Node *leftRotate(struct Node *x)
{
struct Node *y = x->right;
struct Node *T2 = y->left;

// Perform rotation
y->left = x;
x->right = T2;

// Update heights
x->height = max(height(x->left),
height(x->right)) + 1;
y->height = max(height(y->left),
height(y->right)) + 1;

// Return new root


return y;
}

// Get Balance factor of node N


int getBalance(struct Node *N)
{
if (N == NULL)
return 0;
return height(N->left) - height(N->right);
}

// Recursive function to insert a key in the subtree rooted


// with node and returns the new root of the subtree.
struct Node* insert(struct Node* node, int key)
P a g e | 28

{
/* 1. Perform the normal BST insertion */
if (node == NULL)
return(newNode(key));

if (key < node->key)


node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
else // Equal keys are not allowed in BST
return node;

/* 2. Update height of this ancestor node */


node->height = 1 + max(height(node->left),
height(node->right));

/* 3. Get the balance factor of this ancestor


node to check whether this node became
unbalanced */
int balance = getBalance(node);

// If this node becomes unbalanced, then


// there are 4 cases

// Left Left Case


if (balance > 1 && key < node->left->key)
return rightRotate(node);

// Right Right Case


if (balance < -1 && key > node->right->key)
return leftRotate(node);

// Left Right Case


if (balance > 1 && key > node->left->key)
{
node->left = leftRotate(node->left);
return rightRotate(node);
}

// Right Left Case


if (balance < -1 && key < node->right->key)
{
node->right = rightRotate(node->right);
return leftRotate(node);
}

/* return the (unchanged) node pointer */


return node;
}
P a g e | 29

// A utility function to print preorder traversal


// of the tree.
// The function also prints height of every node
void preOrder(struct Node *root)
{
if(root != NULL)
{
printf("%d ", root->key);
preOrder(root->left);
preOrder(root->right);
}
}

/* Driver program to test above function*/


int main()
{
struct Node *root = NULL;

/* Constructing tree given in the above figure */


root = insert(root, 10);
root = insert(root, 20);
root = insert(root, 30);
root = insert(root, 40);
root = insert(root, 50);
root = insert(root, 25);

printf("Preorder traversal before inserting 45 : \n");


preOrder(root);

printf("\n");

root = insert(root, 45);

printf("Preorder traversal after inserting 45 : \n");


preOrder(root);

return 0;
}
P a g e | 30

The time complexity for searching, inserting, and deleting in an AVL tree is O(log n),
where n is the number of nodes in the tree. This is because AVL trees are self-balancing
binary search trees that maintain a height of log n.
In contrast, a binary search tree that is not balanced could degenerate into a linked list in
the worst case, resulting in a time complexity of O(n) for these operations.

Output:
P a g e | 31

Experiment no. 7
Aim: To implement Red Black Tree algorithm on the given set of elements.

/** C implementation for


Red-Black Tree Insertion
This code is provided by
costheta_z **/
#include <stdio.h>
#include <stdlib.h>

// Structure to represent each


// node in a red-black tree
struct node {
int d; // data
int c; // 1-red, 0-black
struct node* p; // parent
struct node* r; // right-child
struct node* l; // left child
};

// global root for the entire tree


struct node* root = NULL;

// function to perform BST insertion of a node


struct node* bst(struct node* trav,
struct node* temp)
{
// If the tree is empty,
// return a new node
if (trav == NULL)
return temp;

// Otherwise recur down the tree


if (temp->d < trav->d)
{
trav->l = bst(trav->l, temp);
trav->l->p = trav;
}
else if (temp->d > trav->d)
{
trav->r = bst(trav->r, temp);
trav->r->p = trav;
}

// Return the (unchanged) node pointer


return trav;
}
P a g e | 32

// Function performing right rotation


// of the passed node
void rightrotate(struct node* temp)
{
struct node* left = temp->l;
temp->l = left->r;
if (temp->l)
temp->l->p = temp;
left->p = temp->p;
if (!temp->p)
root = left;
else if (temp == temp->p->l)
temp->p->l = left;
else
temp->p->r = left;
left->r = temp;
temp->p = left;
}

// Function performing left rotation


// of the passed node
void leftrotate(struct node* temp)
{
struct node* right = temp->r;
temp->r = right->l;
if (temp->r)
temp->r->p = temp;
right->p = temp->p;
if (!temp->p)
root = right;
else if (temp == temp->p->l)
temp->p->l = right;
else
temp->p->r = right;
right->l = temp;
temp->p = right;
}

// This function fixes violations


// caused by BST insertion
void fixup(struct node* root, struct node* pt)
{
struct node* parent_pt = NULL;
struct node* grand_parent_pt = NULL;

while ((pt != root) && (pt->c != 0)


&& (pt->p->c == 1))
{
parent_pt = pt->p;
P a g e | 33

grand_parent_pt = pt->p->p;

/* Case : A
Parent of pt is left child
of Grand-parent of
pt */
if (parent_pt == grand_parent_pt->l)
{

struct node* uncle_pt = grand_parent_pt->r;

/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if (uncle_pt != NULL && uncle_pt->c == 1)
{
grand_parent_pt->c = 1;
parent_pt->c = 0;
uncle_pt->c = 0;
pt = grand_parent_pt;
}

else {

/* Case : 2
pt is right child of its parent
Left-rotation required */
if (pt == parent_pt->r) {
leftrotate(parent_pt);
pt = parent_pt;
parent_pt = pt->p;
}

/* Case : 3
pt is left child of its parent
Right-rotation required */
rightrotate(grand_parent_pt);
int t = parent_pt->c;
parent_pt->c = grand_parent_pt->c;
grand_parent_pt->c = t;
pt = parent_pt;
}
}

/* Case : B
Parent of pt is right
child of Grand-parent of
pt */
else {
P a g e | 34

struct node* uncle_pt = grand_parent_pt->l;

/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if ((uncle_pt != NULL) && (uncle_pt->c == 1))
{
grand_parent_pt->c = 1;
parent_pt->c = 0;
uncle_pt->c = 0;
pt = grand_parent_pt;
}
else {
/* Case : 2
pt is left child of its parent
Right-rotation required */
if (pt == parent_pt->l) {
rightrotate(parent_pt);
pt = parent_pt;
parent_pt = pt->p;
}

/* Case : 3
pt is right child of its parent
Left-rotation required */
leftrotate(grand_parent_pt);
int t = parent_pt->c;
parent_pt->c = grand_parent_pt->c;
grand_parent_pt->c = t;
pt = parent_pt;
}
}
}
}

// Function to print inorder traversal


// of the fixated tree
void inorder(struct node* trav)
{
if (trav == NULL)
return;
inorder(trav->l);
printf("%d ", trav->d);
inorder(trav->r);
}

// driver code
int main()
{
P a g e | 35

int n = 7;
int a[7] = { 7, 6, 5, 4, 3, 2, 1 };

for (int i = 0; i < n; i++) {

// allocating memory to the node and initializing:


// 1. color as red
// 2. parent, left and right pointers as NULL
// 3. data as i-th value in the array
struct node* temp
= (struct node*)malloc(sizeof(struct node));
temp->r = NULL;
temp->l = NULL;
temp->p = NULL;
temp->d = a[i];
temp->c = 1;

// calling function that performs bst insertion of


// this newly created node
root = bst(root, temp);

// calling function to preserve properties of rb


// tree
fixup(root, temp);
root->c = 0;
}

printf("Inorder Traversal of Created Tree\n");


inorder(root);

return 0;
}

Red-black trees have logarithmic average and worst-case time complexity for insertion,
search, and deletion. The time complexity for insertion is (log(N)), where (N) is the
number of nodes in the tree. The average time complexity for rebalancing is (O(1)), and
the worst-case complexity is (O(logn)).

Output:
P a g e | 36

Experiment no. 8
Aim: To implement BTree Algorithm on the given set of elements.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define M 4 // Maximum degree of the B-tree

struct BTreeNode {
int num_keys; // Number of keys currently in the node
int keys[M-1]; // Array of keys
struct BTreeNode *children[M]; // Array of child pointers
bool is_leaf; // True if node is a leaf
};

// Function to create a new node


struct BTreeNode *createNode(bool is_leaf) {
struct BTreeNode *newNode = (struct BTreeNode *)malloc(sizeof(struct BTreeNode));
if (newNode == NULL) {
perror("Memory allocation failed");
exit(EXIT_FAILURE);
}
newNode->num_keys = 0;
newNode->is_leaf = is_leaf;
for (int i = 0; i < M; i++) {
newNode->children[i] = NULL;
}
return newNode;
}

// Function to split a full child node


void splitChild(struct BTreeNode *parent, int index) {
struct BTreeNode *child = parent->children[index];
struct BTreeNode *newNode = createNode(child->is_leaf);

newNode->num_keys = M/2 - 1;

// Move keys and children to the new node


for (int i = 0; i < M/2 - 1; i++) {
newNode->keys[i] = child->keys[i + M/2];
}

if (!child->is_leaf) {
for (int i = 0; i < M/2; i++) {
newNode->children[i] = child->children[i + M/2];
}
P a g e | 37

child->num_keys = M/2 - 1;

// Shift parent's children to make space for the new node


for (int i = parent->num_keys; i > index; i--) {
parent->children[i + 1] = parent->children[i];
}

parent->children[index + 1] = newNode;

// Shift parent's keys to insert the middle key from the child
for (int i = parent->num_keys - 1; i >= index; i--) {
parent->keys[i + 1] = parent->keys[i];
}

parent->keys[index] = child->keys[M/2 - 1];


parent->num_keys++;
}

// Function to insert a key into a non-full node


void insertNonFull(struct BTreeNode *node, int key) {
int i = node->num_keys - 1;

if (node->is_leaf) {
// Insert key into the sorted order
while (i >= 0 && node->keys[i] > key) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = key;
node->num_keys++;
} else {
// Find the child to insert the key
while (i >= 0 && node->keys[i] > key) {
i--;
}
i++;

if (node->children[i]->num_keys == M - 1) {
// Split child if it's full
splitChild(node, i);

// Determine which of the two children is the new one


if (node->keys[i] < key) {
i++;
}
}
insertNonFull(node->children[i], key);
}
P a g e | 38

// Function to insert a key into the B-tree


void insert(struct BTreeNode **root, int key) {
struct BTreeNode *node = *root;

if (node == NULL) {
// Create a new root node
*root = createNode(true);
(*root)->keys[0] = key;
(*root)->num_keys = 1;
} else {
if (node->num_keys == M - 1) {
// Split the root if it's full
struct BTreeNode *new_root = createNode(false);
new_root->children[0] = node;
splitChild(new_root, 0);
*root = new_root;
}
insertNonFull(*root, key);
}
}

// Function to traverse and print the B-tree in-order


void traverse(struct BTreeNode *root) {
if (root != NULL) {
int i;
for (i = 0; i < root->num_keys; i++) {
traverse(root->children[i]);
printf("%d ", root->keys[i]);
}
traverse(root->children[i]);
}
}

// Main function to test B-tree implementation


int main() {
struct BTreeNode *root = NULL;

insert(&root, 10);
insert(&root, 20);
insert(&root, 5);
insert(&root, 6);
insert(&root, 12);
insert(&root, 30);

printf("In-order traversal of the B-tree: ");


traverse(root);
printf("\n");
P a g e | 39

return 0;
}

Time Complexity:

Output:

Experiment no. 9
P a g e | 40

Aim: To implement Greedy Algorithm Approach on Minimum Spanning Tree (MST).

Kruskal Algorithm for Minimum Spanning Tree:

// Kruskal's algorithm in C

#include <stdio.h>

#define MAX 30

typedef struct edge {


int u, v, w;
} edge;

typedef struct edge_list {


edge data[MAX];
int n;
} edge_list;

edge_list elist;

int Graph[MAX][MAX], n;
edge_list spanlist;

void kruskalAlgo();
int find(int belongs[], int vertexno);
void applyUnion(int belongs[], int c1, int c2);
void sort();
void print();

// Applying Krushkal Algo


void kruskalAlgo() {
int belongs[MAX], i, j, cno1, cno2;
elist.n = 0;

for (i = 1; i < n; i++)


for (j = 0; j < i; j++) {
if (Graph[i][j] != 0) {
elist.data[elist.n].u = i;
elist.data[elist.n].v = j;
elist.data[elist.n].w = Graph[i][j];
elist.n++;
}
}

sort();

for (i = 0; i < n; i++)


P a g e | 41

belongs[i] = i;

spanlist.n = 0;

for (i = 0; i < elist.n; i++) {


cno1 = find(belongs, elist.data[i].u);
cno2 = find(belongs, elist.data[i].v);

if (cno1 != cno2) {
spanlist.data[spanlist.n] = elist.data[i];
spanlist.n = spanlist.n + 1;
applyUnion(belongs, cno1, cno2);
}
}
}

int find(int belongs[], int vertexno) {


return (belongs[vertexno]);
}

void applyUnion(int belongs[], int c1, int c2) {


int i;

for (i = 0; i < n; i++)


if (belongs[i] == c2)
belongs[i] = c1;
}

// Sorting algo
void sort() {
int i, j;
edge temp;

for (i = 1; i < elist.n; i++)


for (j = 0; j < elist.n - 1; j++)
if (elist.data[j].w > elist.data[j + 1].w) {
temp = elist.data[j];
elist.data[j] = elist.data[j + 1];
elist.data[j + 1] = temp;
}
}

// Printing the result


void print() {
int i, cost = 0;

for (i = 0; i < spanlist.n; i++) {


printf("\n%d - %d : %d", spanlist.data[i].u, spanlist.data[i].v, spanlist.data[i].w);
cost = cost + spanlist.data[i].w;
P a g e | 42

printf("\nSpanning tree cost: %d", cost);


}

int main() {
int i, j, total_cost;

n = 6;

Graph[0][0] = 0;
Graph[0][1] = 4;
Graph[0][2] = 4;
Graph[0][3] = 0;
Graph[0][4] = 0;
Graph[0][5] = 0;
Graph[0][6] = 0;

Graph[1][0] = 4;
Graph[1][1] = 0;
Graph[1][2] = 2;
Graph[1][3] = 0;
Graph[1][4] = 0;
Graph[1][5] = 0;
Graph[1][6] = 0;

Graph[2][0] = 4;
Graph[2][1] = 2;
Graph[2][2] = 0;
Graph[2][3] = 3;
Graph[2][4] = 4;
Graph[2][5] = 0;
Graph[2][6] = 0;

Graph[3][0] = 0;
Graph[3][1] = 0;
Graph[3][2] = 3;
Graph[3][3] = 0;
Graph[3][4] = 3;
Graph[3][5] = 0;
Graph[3][6] = 0;

Graph[4][0] = 0;
Graph[4][1] = 0;
Graph[4][2] = 4;
Graph[4][3] = 3;
Graph[4][4] = 0;
Graph[4][5] = 0;
Graph[4][6] = 0;
P a g e | 43

Graph[5][0] = 0;
Graph[5][1] = 0;
Graph[5][2] = 2;
Graph[5][3] = 0;
Graph[5][4] = 3;
Graph[5][5] = 0;
Graph[5][6] = 0;

kruskalAlgo();
print();
}

The time complexity of Kruskal's algorithm for finding the minimum spanning tree of a
graph is (O(ElogV)), where (E) is the number of edges in the graph and (V) is the number
of vertices: Sorting edges: This takes (O(ElogE)) time Union-find: This takes at most
(O(logV)) time for every edge (E).

Output:

Experiment no. 10
P a g e | 44

Aim: To implement Knap Sack Problem using Greedy Algorithm approach.

The Knapsack problem is an example of the combinational optimization problem. This


problem is also commonly known as the “Rucksack Problem“. The name of the problem
is defined from the maximization problem as mentioned below:

Given a bag with maximum weight capacity of W and a set of items, each having a
weight and a value associated with it. Decide the number of each item to take in a
collection such that the total weight is less than the capacity and the total value is
maximized.

/* A Naive recursive implementation


of 0-1 Knapsack problem */
#include <stdio.h>

// A utility function that returns


// maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }

// Returns the maximum value that can be


// put in a knapsack of capacity W
int knapSack(int W, int wt[], int val[], int n)
{
// Base Case
if (n == 0 || W == 0)
return 0;

// If weight of the nth item is more than


// Knapsack capacity W, then this item cannot
// be included in the optimal solution
if (wt[n - 1] > W)
return knapSack(W, wt, val, n - 1);

// Return the maximum of two cases:


// (1) nth item included
// (2) not included
else
return max(
val[n - 1]
+ knapSack(W - wt[n - 1], wt, val, n - 1),
knapSack(W, wt, val, n - 1));
}

// Driver code
int main()
{
int profit[] = { 60, 100, 120 };
int weight[] = { 10, 20, 30 };
int W = 50;
P a g e | 45

int n = sizeof(profit) / sizeof(profit[0]);


printf("%d", knapSack(W, weight, profit, n));
return 0;
}

Output:

Time Complexity:

The time complexity of the knapsack algorithm is (O(N*W)), where (N) is the number of
weight elements and (W) is the capacity of the knapsack.

Experiment no. 11
P a g e | 46

Aim: To implement Travelling Salesman Problem using Greedy Approach.

The travelling salesman problem is a graph computational problem where the salesman
needs to visit all cities (represented using nodes in a graph) in a list just once and the
distances (represented using edges in the graph) between all these cities are known. The
solution that is needed to be found for this problem is the shortest possible route in
which the salesman visits all the cities and returns to the origin city.

#include <stdio.h>
int tsp_g[10][10] = {
{12, 30, 33, 10, 45},
{56, 22, 9, 15, 18},
{29, 13, 8, 5, 12},
{33, 28, 16, 10, 3},
{1, 4, 30, 24, 20}
};
int visited[10], n, cost = 0;

/* creating a function to generate the shortest path */


void travellingsalesman(int c){
int k, adj_vertex = 999;
int min = 999;

/* marking the vertices visited in an assigned array */


visited[c] = 1;

/* displaying the shortest path */


printf("%d ", c + 1);

/* checking the minimum cost edge in the graph */


for(k = 0; k < n; k++) {
if((tsp_g[c][k] != 0) && (visited[k] == 0)) {
if(tsp_g[c][k] < min) {
min = tsp_g[c][k];
adj_vertex = k;
}
}
}
if(min != 999) {
cost = cost + min;
}
if(adj_vertex == 999) {
adj_vertex = 0;
printf("%d", adj_vertex + 1);
cost = cost + tsp_g[c][adj_vertex];
return;
}
travellingsalesman(adj_vertex);
}
P a g e | 47

/* main function */
int main(){
int i, j;
n = 5;
for(i = 0; i < n; i++) {
visited[i] = 0;
}
printf("Shortest Path: ");
travellingsalesman(0);
printf("\nMinimum Cost: ");
printf("%d\n", cost);
return 0;
}

Output:

Experiment No. 12
P a g e | 48

Aim: To implement Coin Sequence Problem of finding minimum number of coins


through Greedy Approach.

Given a value of V Rs and an infinite supply of each of the denominations {1, 2, 5, 10, 20,
50, 100, 500, 1000} valued coins/notes, the task is to find the minimum number of coins
and/or notes needed to make the change?

// C program to find minimum


// number of denominations
#include <stdio.h>
#define COINS 9
#define MAX 20

// All denominations of Indian Currency


int coins[COINS] = { 1, 2, 5, 10, 20, 50, 100, 200, 2000 };

void findMin(int cost)


{
int coinList[MAX] = { 0 };
int i, k = 0;

for (i = COINS - 1; i >= 0; i--) {


while (cost >= coins[i]) {
cost -= coins[i];
// Add coin in the list
coinList[k++] = coins[i];
}
}

for (i = 0; i < k; i++) {


// Print
printf("%d ", coinList[i]);
}
return;
}

int main(void)
{
// input value
int n = 93;

printf("Following is minimal number"


"of change for %d: ",
n);
findMin(n);
return 0;
}
P a g e | 49

Output:

You might also like