Daa Practical File
Daa Practical File
(A Govt. Aided UGC Autonomous & NAAC Accredited Institute Affiliated to RGPV, Bhopal)
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.
A. Insertion Sort
#include <iostream>
using namespace std;
// 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:
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;
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:
N * (N+1) / 2 comparisons
N swaps
Hence, the time complexity is O(N^2).
N * (N+1) / 2 comparisons
0 swaps
Note only the number of swaps has changed. Hence, the time complexity is O(N^2).
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:
int count = 0;
for (int i = start + 1; i <= end; i++)
{
if (arr[i] <= pivot)
count++;
}
return pivotIndex;
}
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);
E. Merge Sort
#include <iostream>
using namespace std;
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
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)
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);
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:
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)).
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-
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>
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 - ";
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:
The dominant term in "Average number of comparisons" is N/2. So, the Average Case Time
Complexity of Linear Search is O(N).
#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:
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:
return 0;
}
OUTPUT:
4. WAP to implement Matrix Chain Multiplication and analyze its time
complexity.
#include <iostream>
#include <limits.h>
using namespace std;
int main()
{
int arr[] = {1, 2, 3, 4};
int size = sizeof(arr) / sizeof(arr[0]);
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:
#include <string>
using namespace std;
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)
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:
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:
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>
return min_index;
}
bool sptSet[V];
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = false;
dist[src] = 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.
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:
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:
// 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);
// 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 }
};
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;
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