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

Daa Practical File

The document contains code implementations and analysis of time complexities for several sorting algorithms: 1. Insertion sort, selection sort, bubble sort, quick sort, merge sort, radix sort, and heap sort are implemented using arrays. 2. The time complexities of insertion sort, selection sort, and bubble sort are analyzed to be O(n^2) in the worst case and O(n) or O(n log n) in the best cases. 3. Quicksort is analyzed to have an average and best case time complexity of O(n log n). 4. The document also includes implementations of linear and binary search, as well as algorithms like Strassen's matrix

Uploaded by

Chandan Jat
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)
651 views49 pages

Daa Practical File

The document contains code implementations and analysis of time complexities for several sorting algorithms: 1. Insertion sort, selection sort, bubble sort, quick sort, merge sort, radix sort, and heap sort are implemented using arrays. 2. The time complexities of insertion sort, selection sort, and bubble sort are analyzed to be O(n^2) in the worst case and O(n) or O(n log n) in the best cases. 3. Quicksort is analyzed to have an average and best case time complexity of O(n log n). 4. The document also includes implementations of linear and binary search, as well as algorithms like Strassen's matrix

Uploaded by

Chandan Jat
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/ 49

Madhav Institute of Technology and Science, Gwalior

(A Govt. Aided UGC Autonomous & NAAC Accredited Institute Affiliated to RGPV, Bhopal)

Department of Information Technology

A
Practical File
on
“Design & Analysis of Algorithms”
(270302)

SUBMITTED BY
Chandan Jat
0901AD211009
3rd Semester
ARTIFICIAL INTELLIGENCE AND DATA SCIENCE
Session: 2022-23

SUBMITTED TO
Prof. Abhishek Dixit
Prof. Mir Shahnawaz Ahmad
INDEX
S. No. Name Of Experiment DATE SIGNATURE
WAP to implement the following using
1 array as data structure and analyze its
time complexity.
a) Insertion sort b) Selection sort
c) Bubble sort d) Quick sort
e) Merge sort f) Radix sort
g) Heap sort
WAP to implement Linear and Binary
2 Search and analyze its time complexity.

WAP to implement Strassen’s Matrix


3 Multiplication.
WAP to implement Matrix Chain
4 Multiplication and analyze its time
complexity.
WAP to implement Longest Common
5
Subsequence Problem and analyze its
time complexity.

WAP to implement Optimal Binary


6 Search Tree Problem and analyze its
time complexity.

WAP to implement 0/1 knapsack using


7 dynamic programming.

WAP to implement Dijkstra’s Algorithm


8 and analyze its time complexity.
WAP to implement Bellman Ford
9 Algorithm and analyze its time
complexity.
WAP to implement DFS and BFS and
10
analyze their time complexities.

WAP to implement Travelling Salesman


11
Problem using backtracking.

WAP to implement Topological sort


12
algorithm and analyze their time
complexities.
1. WAP to implement the following using array as data structure and
analyze its time complexity.
a) Insertion sort b) Selection sort c) Bubble sort d) Quick sort e) Merge
sort f) Radix sort g) Heap sort

A. Insertion Sort
#include <iostream>
using namespace std;

// Function to print an array


void printArray(int array[], int size) {
for (int i = 0; i < size; i++) {
cout << array[i] << " ";
}
cout << endl;
}

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


for (int step = 1; step < size; step++) {
int key = array[step];
int j = step - 1;

// Compare key with each element on the left of it until an element smaller than
// it is found.
// For descending order, change key<array[j] to key>array[j].
while (key < array[j] && j >= 0) {
array[j + 1] = array[j];
--j;
}
array[j + 1] = key;
}
}

// Driver code
int main() {
int data[] = {9, 5, 1, 4, 3};
int size = sizeof(data) / sizeof(data[0]);
insertionSort(data, size);
cout << "Sorted array in ascending order:\n";
printArray(data, size);
cout<<"\t\t\t\t\t"<<"Name: Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
}

OUTPUT:

Analysing Time Complexity:


Best Case Analysis
In Best Case i.e., when the array is already sorted, tj = 1
Therefore,T( n ) = C1 * n + ( C2 + C3 ) * ( n - 1 ) + C4 * ( n - 1 ) + ( C5 + C6 ) * ( n - 2 ) +
C8 * ( n - 1 )
which when further simplified has dominating factor of n and gives T(n) = C * ( n ) or O(n)

Worst Case Analysis


In Worst Case i.e., when the array is reversly sorted (in descending order), tj = j
Therefore,T( n ) = C1 * n + ( C2 + C3 ) * ( n - 1 ) + C4 * ( n - 1 ) ( n ) / 2 + ( C5 + C6 ) * ( ( n
- 1 ) (n ) / 2 - 1) + C8 * ( n - 1 )
which when further simplified has dominating factor of n2 and gives T(n) = C * ( n 2) or
O( n^2 )

Average Case Analysis


Let's assume that tj = (j-1)/2 to calculate the average case
Therefore,T( n ) = C1 * n + ( C2 + C3 ) * ( n - 1 ) + C4/2 * ( n - 1 ) ( n ) / 2 + ( C5 + C6 )/2 *
( ( n - 1 ) (n ) / 2 - 1) + C8 * ( n - 1 )

which when further simplified has dominating factor of n2 and gives T(n) = C * ( n^2) or
O( n^2 )
B . Selection Sort
#include <iostream>
using namespace std;

void swap(int *a,int *b)


{
int temp=*a;
*a=*b;
*b=temp;
}

void selection_sort(int arr[], int n)


{
int min;
for (int i=0;i<n-1;i++)
{
min=i;
for (int j=i+1;j<n;j++)
{
if (arr[j]<arr[min])
{
min=j;
}
}
swap(&arr[i],&arr[min]);
}
}

void print_array(int arr[] , int num)


{
for (int i=0;i<num;i++)
{
cout<<arr[i]<<" ";
}
}

int main()
{
cout<<"Selection Sort"<<endl;
int num;
cout<<"Enter number of elements to be entered in array : ";
cin>>num;
int arr[num];
for (int i=0;i<num;i++)
{
cout<<"Entter value :";
cin>>arr[i];
}
cout<<"Array Before Sorting : ";
print_array(arr,num);
cout<<endl;
selection_sort(arr,num);
cout<<"Array after Sorting : ";

print_array(arr,num);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name: Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}
OUTPUT:

Analysing Time Complexity:


Worst Case Time Complexity of Selection Sort:
The cost in this case is that at each step, a swap is done. This is because the smallest element
will always be the last element and the swapped element which is kept at the end will be the
second smallest element that is the smallest element of the new unsorted sub-array. Hence,
the worst case has:

 N * (N+1) / 2 comparisons
 N swaps
Hence, the time complexity is O(N^2).

Best Case Time Complexity of Selection Sort


This is the best case as we can avoid the swap at each step but the time spend to find the
smallest element is still O(N). Hence, the best case has:

 N * (N+1) / 2 comparisons
 0 swaps
Note only the number of swaps has changed. Hence, the time complexity is O(N^2).

Average Case Time Complexity of Selection Sort


Based on the worst case and best case, we know that the number of comparisons will be the
same for every case and hence, for average case as well, the number of comparisons will be
constant.Number of comparisons = N * (N+1) / 2

Therefore, the time complexity will be O(N^2).


C. Bubble Sort
# include <iostream>
using namespace std;

void bubble_sort(int arr[],int n)


{
for (int i=0;i<n;i++)
{
for (int j=0;j<n-1;j++)
{
if (arr[j]>arr[j+1])
{
swap(arr[j],arr[j+1]);
}
}
}
cout<<"Array After Sorting : ";
for (int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}

int main()
{
cout<<"Bubble Sort"<<endl;
int num;
cout<<"Enter number of elements to be entered in array : ";
cin>>num;
int arr[num];
for (int i=0;i<num;i++)
{
cout<<"Entter value :";
cin>>arr[i];
}
cout<<"Array Before Sorting : ";
for (int i=0;i<num;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
bubble_sort(arr,num);
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}
OUTPUT:

Analysing Time Complexity:


Worst Case Time Complexity:
This is the case when the array is reversely sort

Therefore, in the worst case:

 Number of Comparisons: O(N^2) time


 Number of swaps: O(N^2) time
Best Case Time Complexity:
This case occurs when the given array is already sorted.

Therefore, in the best case:

 Number of Comparisons: N = O(N) time


 Number of swaps: 0 = O(1) time
Average Case Time Complexity:
(N-1) + (N-3) + (N-5) ... + 0 + ... + (N-3) + (N-1)
= N x N - 2 x (1 + 3 + 5 + ... + N/2)
= N^2 - 2 x N^2 / 4
= N^2 - N^2 / 2
= N^2 / 2

Therefore, in average, the number of swaps = O(N^2).

Therefore, in the average case time complexity of Bubble sort:

 Number of Comparisons: O(N^2) time


 Number of swaps: O(N^2) time
D. Quick Sort
#include <iostream>
using namespace std;

int partition(int arr[], int start, int end)


{

int pivot = arr[start];

int count = 0;
for (int i = start + 1; i <= end; i++)
{
if (arr[i] <= pivot)
count++;
}

int pivotIndex = start + count;


swap(arr[pivotIndex], arr[start]);

int i = start, j = end;

while (i < pivotIndex && j > pivotIndex)


{

while (arr[i] <= pivot)


{
i++;
}
while (arr[j] > pivot)
{
j--;
}

if (i < pivotIndex && j > pivotIndex)


{
swap(arr[i++], arr[j--]);
}
}

return pivotIndex;
}

void quickSort(int arr[], int start, int end)


{

if (start >= end)


return;
int p = partition(arr, start, end);

quickSort(arr, start, p - 1);

quickSort(arr, p + 1, end);
}
int main()
{
cout<<"Quick Sort"<<endl;
int n;
cout<<"Enter number of elements to be entered in array : ";
cin>>n;
int arr[n];
// int n = 6;
cout<<"Array Before Sorting "<<endl;
for (int i = 0; i < n; i++)
{
cout<<"Enter value: ";
cin >> arr[i];
}
quickSort(arr, 0, n - 1);

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


{
cout << arr[i] << " ";
}
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}
OUTPUT:

Analysing Time Complexity:


Time complexity of Quick sort (Best and Average Case):O (n*logn)
Time complexity of Quick sort (Worst Case): O (n^2)

E. Merge Sort
#include <iostream>
using namespace std;

void display(int *array, int size)


{
for (int i = 0; i < size; i++)
cout << array[i] << " ";
cout << endl;
}
void merge(int *array, int l, int mid, int r)
{
int n1 = mid - l + 1;
int n2 = r - mid;
int a[n1], b[n2];
for (int i = 0; i < n1; i++)
a[i] = array[l + i];
for (int j = 0; j < n2; j++)
b[j] = array[mid + 1 + j];
int i = 0;
int j = 0;
int k = l;
while (i < n1 && j < n2)
{
if (a[i] <= b[j])
{
array[k] = a[i];
i++;
}
else
{
array[k] = b[j];
j++;
}
k++;
}
while (i < n1)
{
array[k] = a[i];
i++;
k++;
}
while (j < n2)
{
array[k] = b[j];
j++;
k++;
}
}
void mergeSort(int *array, int l, int r)
{
int m;
if (l < r)
{
int m = l + (r - l) / 2;
mergeSort(array, l, m);
mergeSort(array, m + 1, r);
merge(array, l, m, r);
}
}
int main()
{
cout << "Merge Sort" << endl;
int n;
cout << "Enter the number of elements: ";
cin >> n;
int arr[n];
for (int i = 0; i < n; i++)
{
cout << "Enter elements:";
cin >> arr[i];
}
cout << "Array before Sorting: ";
display(arr, n);
mergeSort(arr, 0, n - 1);
cout << "Array after Sorting: ";
display(arr, n);
cout << endl;
cout << "\t\t\t\t\t"<< "Name:Chandan Jat" << endl;
cout << "\t\t\t\t\tDate:" << __DATE__ << endl;
cout << "\t\t\t\t\tTime:" << __TIME__ << endl;
}
OUTPUT:
Analysing Time Complexity:
General analysis

T(N) = Time Complexity for problem size N


T(n) = Θ(1) + 2T(n/2) + Θ(n) + Θ(1)
T(n) = 2T(n/2) + Θ(n)

Let us analyze this step by step:


T(n) = 2 * T(n/2) + 0(n)

STEP-1 Is to divide the array into two parts of equal size .


2 * T(n/2) --> Part 1

STEP-2 Now to merge baiscall traverse through all the elements.


constant * n --> Part 2

STEP-3 --> COMBINE 1 + 2


T(n) = 2 * T(n/2) + constant * n --> Part 3

Now we can further divide the array into two halfs if size of the partition arrays are greater
than 1. So,

n/2/2--> n/4
T(N) = 2 * (2 * T(n/4) + constant * n/2) + constant * n
T(N) = 4 * T(n/4) + 2 * constant * n

For this we can say that:


Where n can be subtituted to 2^k and the value of k is logN
T(n) = 2^k * T(n/(2^k)) + k * constant * n

Hence,
T(N) = N * T(1) + N * logN
= O(N * log(N))
F.Radix Sort
#include <iostream>
using namespace std;
void radix_sort(int arr[],int n)
{
int mx=arr[0];
for (int c=0;c<n;c++)
{
if (arr[c]>mx)
{
mx=arr[c];
}
}
for (int exp = 1; mx/ exp > 0; exp *= 10)
{
int res[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--)
{
res[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
for (int i = 0; i < n; i++)
{
arr[i] = res[i];
}
}
}
void print_array(int arr[] , int num)
{
for (int i=0;i<num;i++)
{
cout<<arr[i]<<" ";
}
}
int main()
{
cout<<"Radix Sort"<<endl;
int num;
cout<<"Enter number of elements to be entered in array : ";
cin>>num;
int arr[num];
for (int i=0;i<num;i++)
{
cout<<"Entter value :";
cin>>arr[i];
}
cout<<"Array Before Sorting : ";
print_array(arr,num);
cout<<endl;
radix_sort(arr,num);
cout<<"Array after Sorting : ";
print_array(arr,num);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}
OUTPUT:

Analysing Time
Complexity:
Best case time complexity: Ω(nk)

Average case time complexity: Θ(nk)

Worst case time complexity: O(nk)


Where k is number of digits of maximum number.
G.Heap Sort
#include <iostream>
using namespace std;
void heapify(int arr[], int n, int i)
{
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;

if (r < n && arr[r] > arr[largest])


largest = r;

if (largest != i)
{
swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapSort(int arr[], int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);

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


{
swap(arr[0], arr[i]);

heapify(arr, i, 0);
}
}
void printArray(int arr[], int n)
{
for (int i = 0; i < n; ++i)
cout << arr[i] << " ";
cout << "\n";
}
int main()
{
cout<<"Heap Sort"<<endl;
int n;
cout<<"Enter number of elements to be entered in array : ";
cin>>n;
int arr[n];
for (int i=0;i<n;i++)
{
cout<<"Entter value :";
cin>>arr[i];
}
cout<<"Array Before Sorting: ";
printArray(arr, n);
heapSort(arr, n);
cout << "Sorted array is: ";
printArray(arr, n);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}

OUTPUT:

Analysing Time Complexity:

Worst Case Time Complexity of Heap Sort


The worst case for heap sort might happen when all elements in the list are distinct.
Therefore, we would need to call max-heapify every time we remove an element. In such a
case, considering there are 'n' number of nodes-

 The number of swaps to remove every element would be log(n), as that is the max height of
the heap
 Considering we do this for every node, the total number of moves would be n * (log(n)).
Therefore, the runtime in the worst case will be O(n(log(n)).

Best Case Time Complexity of Heap Sort


The best case for heapsort would happen when all elements in the list to be sorted are
identical. In such a case, for 'n' number of nodes-
 Removing each node from the heap would take only a constant runtime, O(1). There would
be no need to bring any node down or bring max valued node up, as all items are identical.
 Since we do this for every node, the total number of moves would be n * O(1).
Therefore, the runtime in the best case would be O(n).

Average Case Time Complexity of Heap Sort


In terms of total complexity, we already know that we can create a heap in O(n) time and do
insertion/removal of nodes in O(log(n)) time. In terms of average time, we need to take into
account all possible inputs, distinct elements or otherwise. If the total number of nodes is 'n',
in such a case, the max-heapify function would need to perform:

 log(n)/2 comparisons in the first iteration (since we only compare two values at a time to
build max-heap)
 log(n-1)/2 in the second iteration
 log(n-2)/2 in the third iteration
 and so on
So mathematically, the total sum would turn out to be-

(log(n))/2 + (log(n-1))/2 + (log(n-2))/2 + (log(n-3))/2 + ...


Upon approximation, the final result would be
=1/2(log(n!))
=1/2(n∗ log(n)−n+O(log(n)))

Considering the highest ordered term, the average runtime of max-heapify would then be
O(n(log(n)).

Since we call this function for all nodes in the final heapsort function, the runtime would be
(n * O(n(log(n))). Calculating the average, upon dividing by n, we'd get a final average
runtime of O(n(log(n))
2. WAP to implement Linear and Binary Search and analyze its time
complexity.
A. LINEAR SEARCH
#include <iostream>
#include <ctime>

using namespace std;


int linearSearch(int a[], int n, int val) {
for (int i = 0; i < n; i++)
{
if (a[i] == val)
return i+1;
}
return -1;
}
int main() {

int a[1000];
int val;
int n;
//take array
cout<<"ENTER SIZE OF ARRAY: ";

cin>>n;
cout<<"ENTER ARRAY ELEMENTS ";
cout<<endl;
for(int i=0;i<n;i++)
{

cin>>a[i];
}
cout<<"ENTER ITEM TO BE SEARCHED: ";
cin>>val;
int res = linearSearch(a, n, val); // Store result
cout<<"The elements of the array are - ";

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


cout<<a[i]<<" ";
cout<<"\nElement to be searched is - "<<val;
if (res == -1)
cout<<"\nElement is not present in the array";

else
cout<<"\nElement is present at "<<res<<" position of array";
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;

cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;

return 0;
}
OUTPUT:

Analysing Time Complexity:

Analysis of Best Case Time Complexity of Linear Search


The Best Case will take place if:
 The element to be search is on the first index.
The number of comparisons in this case is 1. Thereforce, Best Case Time Complexity of Linear Search is
O(1).

Analysis of Average Case Time Complexity of Linear Search


Let there be N distinct numbers: a1, a2, ..., a(N-1), aN

We need to find element P.

There are two cases:

 Case 1: The element P can be in N distinct indexes from 0 to N-1.


 Case 2: There will be a case when the element P is not present in the list.
There are N case 1 and 1 case 2. So, there are N+1 distinct cases to consider in total.If
element P is in index K, then Linear Search will do K+1 comparisons.

Number of comparisons for all cases in case 1 = Comparisons if element is in index 0 +


Comparisons if element is in index 1 + ... + Comparisons if element is in index N-1
= 1 + 2 + ... + N
= N * (N+1) / 2 comparisons

If element P is not in the list, then Linear Search will do N comparisons.

Number of comparisons for all cases in case 2 = N

Therefore, total number of comparisons for all N+1 cases = N * (N+1) / 2 + N


= N * ((N+1)/2 + 1)

Average number of comparisons = ( N * ((N+1)/2 + 1) ) / (N+1)


= N/2 + N/(N+1)

The dominant term in "Average number of comparisons" is N/2. So, the Average Case Time
Complexity of Linear Search is O(N).

Analysis of Worst Case Time Complexity of Linear Search


The worst case will take place if:

 The element to be search is in the last index


 The element to be search is not present in the list
In both cases, the maximum number of comparisons take place in Linear Search which is
equal to N comparisons.

Hence, the Worst Case Time Complexity of Linear Search is O(N).

Number of Comparisons in Worst Case: N


B. Binary Search

#include <iostream>
#include <ctime>
using namespace std;
int binarySearch(int a[], int beg, int end, int val)
{
int mid;
if(end >= beg)
{ mid = (beg + end)/2;
/* if the item to be searched is present at middle */
if(a[mid] == val)
{
return mid+1;
}
/* if the item to be searched is smaller than middle, then it can only be in left subarray */
else if(a[mid] < val)
{
return binarySearch(a, mid+1, end, val);
}
/* if the item to be searched is greater than middle, then it can only be in right subarray */

else
{
return binarySearch(a, beg, mid-1, val);
}
}
return -1;
}
int main() {
int a[] = {11, 14, 25, 30, 40, 41, 52, 57, 70}; // given array
int val = 40; // value to be searched
int n = sizeof(a) / sizeof(a[0]); // size of array
int res = binarySearch(a, 0, n-1, val); // Store result
printf("The elements of the array are - ");
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\nElement to be searched is - %d", val);
if (res == -1)
printf("\nElement is not present in the array");
else
printf("\nElement is present at %d position of array", res);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}

OUTPUT:

Analysing Time Complexity:


Average Case Time Complexity of Binary Search

Total number of comparisons = 1 * (1) + 2 * (2) + 3 * (4) + ... + logN * (2^(logN-1))

Total number of comparisons = 1 + 4 + 12 + 32 + ... = 2^logN * (logN - 1) + 1

Total number of comparisons = N * (logN - 1) + 1

Total number of cases = N+1

Therefore, average number of comparisons = ( N * (logN - 1) + 1 ) / (N+1)

Average number of comparisons = N * logN / (N+1) - N/(N+1) + 1/(N+1)

The dominant term is N * logN / (N+1) which is approximately logN. Therefore, Average Case
Time Complexity of Binary Search is O(logN).
Best Case Time Complexity of Binary Search
The best case of Binary Search occurs when:

 The element to be search is in the middle of the list


In this case, the element is found in the first step itself and this involves 1 comparison.

Therefore, Best Case Time Complexity of Binary Search is O(1).

Analysis of Worst Case Time Complexity of Binary Search


The worst case of Binary Search occurs when:

 The element is to search is in the first index or last index


In this case, the total number of comparisons required is logN comparisons.

Therefore, Worst Case Time Complexity of Binary Search is O(logN).


3.WAP to implement Strassen’s Matrix Multiplication.
#include<iostream>
using namespace std;
double a[4][4];
double b[4][4];
void insert(double x[4][4])
{
double val;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
cin>>val;
x[i][j]=val;
}
}
}
double cal11(double x[4][4])
{
return (x[1][1] * x[1][2])+ (x[1][2] * x[2][1]);
}
double cal21(double x[4][4]){
return (x[3][1] * x[4][2])+ (x[3][2] * x[4][1]);
}
double cal12(double x[4][4])
{
return (x[1][3] * x[2][4])+ (x[1][4] * x[2][3]);
}
double cal22(double x[4][4])
{
return (x[2][3] * x[1][4])+ (x[2][4] * x[1][3]);
}
int main(){
double a11,a12,a22,a21,b11,b12,b21,b22,a[4][4],b[4][4];
double p,q,r,s,t,u,v,c11,c12,c21,c22;
//insert values in the matrix a
cout<<"\n a: \n";
insert(a);
//insert values in the matrix a
cout<<"\n b: \n";
insert(b);

//dividing single 4x4 matrix into four 2x2 matrices


a11=cal11(a);
a12=cal12(a);
a21=cal21(a);
a22=cal22(a);
b11=cal11(b);
b12=cal12(b);
b21=cal21(b);
b22=cal22(b);

//assigning variables acc. to strassen's algo


p=(a11+a22)*(b11+b22);
q=(a21+a22)*b11;
r=a11*(b12-b22);
s=a22*(b21-b11);
t=(a11+a12)*b22;
u=(a11-a21)*(b11+b12);
v=(a12-a22)*(b21+b22);

//outputting the final matrix


cout<<"\n final matrix";
cout<<"\n"<<p+s-t+v<<" "<<r+t;
cout<<"\n"<<q+s<<" "<<p+r-q+u;
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;

return 0;
}
OUTPUT:
4. WAP to implement Matrix Chain Multiplication and analyze its time
complexity.

#include <iostream>
#include <limits.h>
using namespace std;

int MatrixChainOrder(int p[], int n)


{
int m[n][n];
int i, j, k, L, q;
for (i = 1; i < n; i++)
m[i][i] = 0;
for (L = 2; L < n; L++)
{
for (i = 1; i < n - L + 1; i++)
{
j = i + L - 1;
m[i][j] = INT_MAX;
for (k = i; k <= j - 1; k++)
{
q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (q < m[i][j])
m[i][j] = q;
}
}
}
return m[1][n - 1];
}

int main()
{
int arr[] = {1, 2, 3, 4};
int size = sizeof(arr) / sizeof(arr[0]);

cout << "Minimum number of multiplications is "


<< MatrixChainOrder(arr, size);

getchar();
cout << endl;
cout << "\t\t\t\t\t" << "Name:Chandan Jat " << endl;
cout << "\t\t\t\t\tDate:" << __DATE__ << endl;
cout << "\t\t\t\t\tTime:" << __TIME__ << endl;
return 0;
}

OUTPUT:

Analysing Time Complexity:


Time Complexity: O(N^3 )
5) . WAP to implement Longest Common Subsequence Problem and
analyze its time complexity.
#include <iostream>

#include <string>
using namespace std;

int LCSLength(string X, string Y, int m, int n)


{
if (m == 0 || n == 0)
{
return 0;
}
if (X[m - 1] == Y[n - 1])
{
return LCSLength(X, Y, m - 1, n - 1) + 1;
}
return max(LCSLength(X, Y, m, n - 1), LCSLength(X, Y, m - 1, n));
}

int main()
{
string X = "ABCBDAB", Y = "BDCABA";
cout << "The length of the LCS is " <<LCSLength(X, Y, X.length(), Y.length());
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}

OUTPUT:
Analysing Time Complexity:
Time complexity of the above naive recursive approach is O(2^n)

 Time complexity of dynamic programming: O(n*m) in all three cases.

6) . WAP to implement Optimal Binary Search Tree Problem and analyze


its time complexity.
#include <bits/stdc++.h>
using namespace std;
int sum(int freq[], int i, int j);
int optCost(int freq[], int i, int j)
{
if (j < i)
return 0;
if (j == i)
return freq[i];

int fsum = sum(freq, i, j);

int min = INT_MAX;


for (int r = i; r <= j; ++r)
{
int cost = optCost(freq, i, r - 1) +
optCost(freq, r + 1, j);
if (cost < min)
min = cost;
}
return min + fsum;
}
int optimalSearchTree(int keys[],int freq[], int n)
{
return optCost(freq, 0, n - 1);
}

int sum(int freq[], int i, int j)


{
int s = 0;
for (int k = i; k <= j; k++)
s += freq[k];
return s;
}

int main()
{
int keys[] = {10, 12, 20, 30};
int freq[] = {34, 8, 50, 12};
int n = sizeof(keys) / sizeof(keys[0]);
cout<<"The cost of optimal BST are: "<<endl;
cout << "Cost of Optimal BST is "<< optimalSearchTree(keys, freq, n);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;

return 0;
}

OUTPUT:

Analysing Time Complexity:


Time complexity of the above naive recursive approach is exponential.
The time complexity using Dynamic programming solution is O(n^3)
7) . WAP to implement 0/1 knapsack using dynamic programming.
#include <iostream>
#include <vector>
using namespace std;

int max(int a, int b)


{
return (a > b) ? a : b;
}

int knapSack(int W, int wt[], int val[], int n)


{
int i, w;
vector<vector<int>> K(n + 1, vector<int>(W + 1));
for (i = 0; i <= n; i++)
{
for (w = 0; w <= W; w++)
{
if (i == 0 || w == 0)
K[i][w] = 0;
else if (wt[i - 1] <= w)
K[i][w] = max(val[i - 1] +
K[i - 1][w - wt[i - 1]],
K[i - 1][w]);
else
K[i][w] = K[i - 1][w];
}
}
return K[n][W];
}

int main()
{
int val[] = {60, 100, 120, 130};
int wt[] = {10, 20, 30, 40};
int W = 50;
int n = sizeof(val) / sizeof(val[0]);
cout<<"knapsack using dynamic programming after solve: "<<endl;
cout << knapSack(W, wt, val, n);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat "<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;

return 0;
}
OUTPUT:

Analysing Time Complexity:


Time Complexity: O(N*W).

where ‘N’ is the number of weight element and ‘W’ is capacity. As for every weight
element we traverse through all weight capacities 1<=w<=W.
8) . WAP to implement Dijkstra’s Algorithm and analyze its time
complexity.
#include <iostream>
using namespace std;
#include <limits.h>

// Number of vertices in the graph


#define V 9

int minDistance(int dist[], bool sptSet[])


{

// Initialize min value


int min = INT_MAX, min_index;

for (int v = 0; v < V; v++)


if (sptSet[v] == false && dist[v] <= min)
min = dist[v], min_index = v;

return min_index;
}

void printSolution(int dist[])


{
cout << "Vertex \t Distance from Source" << endl;
for (int i = 0; i < V; i++)
cout << i << " \t\t\t\t" << dist[i] << endl;
}
void dijkstra(int graph[V][V], int src)
{
int dist[V];

bool sptSet[V];
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = false;

dist[src] = 0;

// Find shortest path for all vertices


for (int count = 0; count < V - 1; count++)
{
int u = minDistance(dist, sptSet);
sptSet[u] = true;
for (int v = 0; v < V; v++)
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] <
dist[v])
dist[v] = dist[u] + graph[u][v];
}
printSolution(dist);
}
int main()
{
int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 6, 0},
{4, 0, 6, 0, 0, 0, 0, 11, 0},
{0, 6, 0, 7, 0, 4, 0, 0, 2},
{0, 0, 7, 0, 9, 14, 0, 0, 0},
{0, 0, 0, 9, 0, 10, 0, 0, 0},
{0, 0, 4, 14, 10, 0, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 6},
{6, 11, 0, 0, 0, 0, 1, 0, 7},
{0, 0, 2, 0, 0, 0, 6, 7, 0}};

dijkstra(graph, 0);
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}

OUTPUT:
Analysing Time Complexity:
The time Complexity of the above implementation using adjacency list is O(V^2).

If the input graph is represented using adjacency list, it can be reduced to O(E * log V)
with the help of a binary heap.

9) . WAP to implement Bellman Ford Algorithm and analyze its time


complexity
#include <bits/stdc++.h>
using namespace std;
void BellmanFord(int graph[][3], int V, int E, int src)
{
int dis[V];
for (int i = 0; i < V; i++)
dis[i] = INT_MAX;
dis[src] = 0;
for (int i = 0; i < V - 1; i++)
{

for (int j = 0; j < E; j++)


{
if (dis[graph[j][0]] != INT_MAX && dis[graph[j][0]] + graph[j][2] <
dis[graph[j][1]])
dis[graph[j][1]] =
dis[graph[j][0]] + graph[j][2];
}
}
for (int i = 0; i < E; i++)
{
int x = graph[i][0];
int y = graph[i][1];
int weight = graph[i][2];
if (dis[x] != INT_MAX &&
dis[x] + weight < dis[y])
cout << "Graph contains negative"
" weight cycle"
<< endl;
}
cout << "Vertex Distance from Source" << endl;
for (int i = 0; i < V; i++)
cout << i << "\t\t" << dis[i] << endl;
}
int main()
{
int V = 5;
int E = 8;
int graph[][3] = {{0, 1, -1}, {0, 2, 4}, {1, 2, 3}, {1, 3, 2}, {1, 4, 2}, {3, 2, 5}, {3, 1, 1}, {4,
3, -3}};

BellmanFord(graph, V, E, 0);
cout << endl;
cout << "\t\t\t\t\t"
<< "Name:Chandan Jat" << endl;
cout << "\t\t\t\t\tDate:" << __DATE__ << endl;
cout << "\t\t\t\t\tTime:" << __TIME__ << endl;
return 0;
}

OUTPUT:

Analysing Time Complexity:


Time Complexity of average case: O(V*E)
Time Complexity of best case: O(E)
Time Complexity of worst case: O(V^3)
where:

V is number of vertices
E is number of edges
10. WAP to implement DFS and BFS and analyze their time complexities.
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
//add the edge in graph
void edge(vector<int>adj[],int u,int v){
adj[u].push_back(v);
}
//function for bfs traversal
void bfs(int s,vector<int>adj[],bool visit[]){
queue<int>q;//queue in STL
q.push(s);
visit[s]=true;
while(!q.empty()){
int u=q.front();
cout<<u<<" ";
q.pop();
//loop for traverse
for(int i=0;i<adj[u].size();i++){
if(!visit[adj[u][i]]){
q.push(adj[u][i]);
visit[adj[u][i]]=true;
}
}
}
}
//function for dfs traversal
void dfs(int s,vector<int>adj[],bool visit[]){
stack<int>stk;//stack in STL
stk.push(s);
visit[s]=true;
while(!stk.empty()){
int u=stk.top();
cout<<u<<" ";
stk.pop();
//loop for traverse
for(int i=0;i<adj[u].size();i++){
if(!visit[adj[u][i]]){
stk.push(adj[u][i]);
visit[adj[u][i]]=true;
}
}
}
}
int main(){
vector<int>adj[5];
bool visit[5];
for(int i=0;i<5;i++){
visit[i]=false;
}
edge(adj,0,2);
edge(adj,0,1);
edge(adj,1,3);
edge(adj,2,0);
edge(adj,2,3);
edge(adj,2,4);
cout<<"BFS traversal is"<<" ";
bfs(0,adj,visit);
cout<<endl;
for(int i=0;i<5;i++){
visit[i]=false;
}
cout<<"DFS traversal is"<<" ";
dfs(0,adj,visit);
cout << endl;
cout << "\t\t\t\t\t"<< "Name:Chandan Jat" << endl;
cout << "\t\t\t\t\tDate:" << __DATE__ << endl;
cout << "\t\t\t\t\tTime:" << __TIME__ << endl;

OUTPUT:

Analysing Time Complexity:


Since we are only iterating over the graph’s edges and vertices only once :
Time Complexity of BFS = O(V+E) where V is vertices and E is edges.

Time Complexity of DFS is also O(V+E) where V is vertices and E is edges.

11. WAP to implement Travelling Salesman Problem using backtracking.


// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
#define V 4

// Function to find the minimum weight Hamiltonian Cycle


void tsp(int graph[][V], vector<bool>& v, int currPos,
int n, int count, int cost, int& ans)
{

// If last node is reached and it has a link


// to the starting node i.e the source then
// keep the minimum value out of the total cost
// of traversal and "ans"
// Finally return to check for more possible values
if (count == n && graph[currPos][0]) {
ans = min(ans, cost + graph[currPos][0]);
return;
}

// BACKTRACKING STEP
// Loop to traverse the adjacency list
// of currPos node and increasing the count
// by 1 and cost by graph[currPos][i] value
for (int i = 0; i < n; i++) {
if (!v[i] && graph[currPos][i]) {

// Mark as visited
v[i] = true;
tsp(graph, v, i, n, count + 1,
cost + graph[currPos][i], ans);

// Mark ith node as unvisited


v[i] = false;
}
}
};

// Driver code
int main()
{
// n is the number of nodes i.e. V
int n = 4;

int graph[][V] = {
{ 0, 10, 15, 20 },
{ 10, 0, 35, 25 },
{ 15, 35, 0, 30 },
{ 20, 25, 30, 0 }
};

// Boolean array to check if a node


// has been visited or not
vector<bool> v(n);
for (int i = 0; i < n; i++)
v[i] = false;

// Mark 0th node as visited


v[0] = true;
int ans = INT_MAX;

// Find the minimum weight Hamiltonian Cycle


tsp(graph, v, 0, n, 1, 0, ans);

// ans is the minimum weight Hamiltonian Cycle


cout << “The cost of most efficient tour = ”<<ans;

cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;

return 0;
}

OUTPUT:
Analysing Time Complexity:
O(N!), As for the first node there are N possibilities and for the second node there are n – 1
possibilities.
For N nodes time complexity = N * (N – 1) * . . . 1 = O(N!)

12. WAP to implement Topological sort algorithm and analyze their time
complexities.
#include <iostream>
#include <list>
#include <iterator>
#include <stack>
using namespace std;
class Graph
{
int V;
list<int> *adj;
void topologicalSortUtil(int v, bool visited[], stack<int> &Stack);

public:
Graph(int V);
void addEdge(int v, int w);
void topologicalSort();
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
void Graph::addEdge(int v, int w)
{

adj[v].push_back(w);
}
void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &stack)
{
visited[v] = true;
list<int>::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i)
if (!visited[*i])
topologicalSortUtil(*i, visited, stack);
stack.push(v);
}
void Graph::topologicalSort()
{
stack<int> stack;
bool *visited = new bool[V];
for (int i = 0; i < V; i++)
visited[i] = false;

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


if (visited[i] == false)
topologicalSortUtil(i, visited, stack);
while (stack.empty() == false)
{
cout << stack.top() << " ";
stack.pop();
}
}
int main()
{
Graph g(6);
g.addEdge(5, 2);
g.addEdge(5, 0);
g.addEdge(4, 0);
g.addEdge(4, 1);
g.addEdge(2, 3);
g.addEdge(3, 1);
cout << " Topological Sort of graph" << endl;
g.topologicalSort();
cout<<endl;
cout<<"\t\t\t\t\t"<<"Name:Chandan Jat"<<endl;
cout<<"\t\t\t\t\tDate:"<<__DATE__<<endl;
cout<<"\t\t\t\t\tTime:"<<__TIME__<<endl;
return 0;
}

OUTPUT:
Analysing Time Complexity:
O(V+E). The above algorithm is simply DFS with an extra stack. So time complexity is the
same as DFS

You might also like