DAA Practical File
DAA Practical File
DAA Practical File
Date of
Exp.No Title of the Program Execution Signature
1. Programming that uses recurrence relations to analyze
recursive algorithms.
2. Computing best, average, and worst case time
complexity of various sorting techniques
1
d) Branch and bound algorithm.
2
Program No.1
Programming that uses recurrence relations to analyze recursive algorithms.
A recurrence relation is an equation that defines a sequence where any term is defined in terms of
its previous terms.
The recurrence relations for the time complexity of some problems are given below:
Fibonacci Number
T(N) = T(N/2) + C
Base Condition: T(1) = 1
Merge Sort
T(N) = 2 T(N/2) + CN
Base Condition: T(1) = 1
Recursive Algorithm: Finding min and max in an array
T(N) = 2 T(N/2) + 2
Base Condition: T(1) = 0and T(2)= 1
Karastuba algorithm for fast multiplication
T(N) = 3 T(N/2) + CN
Base Condition: T(0) = 1and T(1)= 1
Quick Sort
Worst Case: This is a case of the unbalanced partition where the partition process always picks
the greatest or smallest element as a pivot(Think!).For the recurrence relation of the worst case
scenario, we can put i = 0 in the above equation.
3
T(N) = T(0) + T(N-1) + CN
which is equivalent to
T(N)= T(N-1) + CN
Best Case: This is a case of the balanced partition where the partition process always picks the
middle element as pivot. For the recurrence relation of the worst case scenario, put i = N/2 in the
above equation.
4
Program No. 2
Computing best, average, and worst case time complexity of various sorting techniques.
1. Bubble Sort
In bubble sort, we compare each adjacent pair. If they are not in the correct order, we swap them.
We keep repeating the previous step until no swaps is needed, which indicates all the elements are
sorted.
Time Complexity:
Space Complexity:
Since we use only a constant amount of additional memory apart from the input array, the space
complexity is O(1).
2. Selection Sort
5
Selection sort is a simple sorting algorithm that divides the array into two parts: a subarray of
already sorted elements and a subarray of remaining elements to be sorted. The sorted subarray is
initially empty. We iterate over the array (n - 1) times. In each iteration, we find the smallest
element from the unsorted subarray and place it at the end of the sorted subarray.
Time Complexity:
In the first iteration, we perform (n - 1) comparisons, (n - 2) in the second, and so on until the last
iteration where we perform only one comparison. Thus the total number of comparisons sum up to
n * (n - 1) / 2. The number of swaps performed is at most n - 1. So the overall time complexity is
quadratic.
Space Complexity:
Since we are not using any extra data structure apart from the input array, the space complexity
is O(1).
3. Insertion Sort
Like selection sort, the insertion sort algorithm also divides the array into two parts: a subarray of
already sorted elements and a subarray of remaining elements to be sorted. The sorted subarray
initially contains only the first element of the array. We iterate over each of the remaining elements
and put them in their correct position in the sorted subarray.
Time Complexity:
Space Complexity:
Since we use only a constant amount of additional memory apart from the input array, the space
complexity is O(1).
6
4. Merge Sort
In merge sort, we divide the unsorted array into n subarrays, each of size one, which can be
considered sorted trivially. Then, we repeatedly merge the sorted subarrays to produce new sorted
subarrays until there is only one sorted array of size n remaining.
Time Complexity:
Merge sort performs the same number of operations for any input array of a given size.
In this algorithm, we keep dividing the array into two subarrays recursively which will create O(log
n) rows where each element is present in each row exactly once.
For each row, it takes O(n) time to merge every pair of subarrays. So the overall time complexity
becomes O(n log n).
Space Complexity:
Since we use an auxiliary array of size at most n to store the merged subarray, the space complexity
is O(n).
5. Quicksort
Quicksort is a relatively more complex algorithm. It uses a divide-and-conquer strategy to divide
the array into two subarrays. We choose an element called pivot and we then place it in its correct
index and then reorder the array by moving all the elements less than pivot to its left and all the
elements greater than it to its right.
We then recursively sort the subarrays of these subarrays until the entire array is sorted. The
efficiency of the quicksort algorithm heavily depends on the selection of the pivot element.
Time Complexity:
Also when there are a large number of identical elements in the array, optimal partitioning becomes
hard, resulting in quadratic time complexity.
7
partitioning every time. Such partitioning allows us to divide the array in half every time.
We can avoid the worst-case in quicksort almost always by choosing an appropriate pivot. There
are various ways to achieve this:
Space Complexity:
Although quicksort doesn’t use auxiliary space to store array elements, additional space is required
for creating stack frames in recursive calls.
6. Heap Sort
In heap sort, we convert the array into a heap. Then we keep extracting the maximum element
from the heap and place it accordingly. Heap sort reconstructs the heap after each extraction.
Time Complexity:
The order of time taken by the heap sort algorithm for an array of any given size is the same.
The process of extraction in a heap structure with n elements takes logarithmic time, O(log n).
When there are n elements in the heap it takes O(log n) time to extract, then there remain (n - 1)
elements, the next extraction takes O(log (n - 1)), and so on until there is only one element in the
heap where the extraction takes O(log 1) time.
8
The total time complexity sums up to O(log n) + O(log (n -1)) + … + O(1) = O(log (n!)). The time
complexity of O(n log n) best represents this complexity in a simplified form.
Space Complexity:
Since we are not using any extra data structure, heap sort is an in-place sorting algorithm.
Therefore, its space complexity is O(1).
9
Program No.3
Performance analysis of different internal and external sorting algorithms with different type
of data set.
We consider sorting a list of records, either into ascending or descending order, based upon the
value of some field of the record we will call the sort key. The list may be contiguous and randomly
accessible (e.g., an array), or it may be dispersed and only sequentially accessible (e.g., a linked
list). The same logic applies in both cases, although implementation details will differ. When
analyzing the performance of various sorting algorithms we will generally consider two factors: -
the number of sort key comparisons that are required - the number of times records in the list must
be moved Both worst-case and average-case performance is significant.\
Internal or External?
In an internal sort, the list of records is small enough to be maintained entirely in physical memory
for the duration of the sort. In an external sort, the list of records will not fit entirely into physical
memory at once. In that case, the records are kept in disk files and only a selection of them are
resident in physical memory at any given time. We will consider only internal sorting at this time.
Sorted Lists
Sorting a list requires accessing the data elements stored in the list. For efficiency this suggests that
the sort capability should be provided via member functions of the list class in question. We will
follow that approach here, although the alternative is certainly feasible. Building upon the
LinkListT or ArrayT classes discussed earlier, we could derive sorted variants, overriding some
member functions of the base class and adding new member functions for sorting. If we do that,
what base member functions must be overridden? - The insertion functions must now take into
account the ordering of the list elements, placing each new element in the proper location. - A
number of the base member functions are unsuitable for a sorted list type and must be disabled. -
The search functions may now take advantage of the list element ordering.
Generic Linked
List Interface template class LinkListT { protected: LinkNodeT* Head; // points to head node in list
LinkNodeT* Tail; // points to tail node in list LinkNodeT* Curr; // points to "current" node in list
public: LinkListT(); LinkListT(const LinkListT& Source); LinkListT& operator=(const
LinkListT& Source); ~LinkListT(); bool isEmpty() const; bool inList() const; bool
PrefixNode(Item newData); bool AppendNode(Item newData); bool InsertAfterCurr(Item
newData); bool Advance(); void gotoHead(); void gotoTail(); bool MakeEmpty(); bool
DeleteCurrentNode(); bool DeleteValue(Item Target); Item getCurrentData() const; void
PrintList(ostream& Out);
10
Program No. 4
Use of divide and conquer technique to solve some problem that uses two different algorithm
for solving small problem.
// Driver code
int main()
{
int arr[7] = { 70, 250, 50, 80, 140, 12, 14 };
int n = sizeof(arr) / sizeof(arr[0]);
int max, min;
max = DAC_Max(arr, 0, n);
min = DAC_Min(arr, 0, n);
cout << "Maximum: " << max << endl;
cout << "Minimum: " << min << endl;
return 0;
}
12
Program No. 5
Implementation of different basic computing algorithms like Hash tables, including collision-
avoidance strategies, Search trees (AVL and B-trees).
#include <stdio.h>
#include <stdlib.h>
struct set
{
int key;
int data;
};
struct set *array;
int capacity = 10;
int size = 0;
int size_of_hashtable()
{
return size;
}
int main()
{
int choice, key, data, n;
int c = 0;
init_array();
do
{
printf("1.Insert item in the Hash Table"
"\n2.Remove item from the Hash Table"
"\n3.Check the size of Hash Table"
"\n4.Display a Hash Table"
"\n\n Please enter your choice: ");
scanf("%d", &choice);
switch (choice)
{
case 1:
break;
case 2:
break;
case 3:
n = size_of_hashtable();
printf("Size of Hash Table is-:%d\n", n);
break;
case 4:
display();
break;
default:
printf("Invalid Input\n");
}
} while (c == 1);
}
16
Program No.6
Consider the problem of eight queens on an (8x8) chessboard. Two queens are said to attack
each other if they are on the same row, column, or diagonal. Write a program that
implements backtracking algorithm to solve the problem i.e. place eight non-attacking queens
on the board.
#define N 4
#include <stdbool.h>
#include <stdio.h>
return true;
}
18
bool solveNQUtil(int board[N][N], int col)
{
// Base case: If all queens are placed
// then return true
if (col >= N)
return true;
19
// If the queen cannot be placed in any row in
// this column col then return false
return false;
}
if (solveNQUtil(board, 0) == false) {
printf("Solution does not exist");
return false;
}
printSolution(board);
return true;
20
}
21
Program No. 7
#include <iostream>
#include <list>
#include <stack>
class Graph
// time
public:
Graph(int V);
// components
void printSCCs();
Graph getTranspose();
};
Graph::Graph(int V)
this->V = V;
visited[v] = true;
list<int>::iterator i;
if (!visited[*i])
DFSUtil(*i, visited);
Graph Graph::getTranspose()
Graph g(V);
list<int>::iterator i;
g.adj[*i].push_back(v);
return g;
23
// Mark the current node as visited and print it
visited[v] = true;
list<int>::iterator i;
if(!visited[*i])
Stack.push(v);
// The main function that finds and prints all strongly connected
// components
void Graph::printSCCs()
stack<int> Stack;
visited[i] = false;
if(visited[i] == false)
Graph gr = getTranspose();
visited[i] = false;
int v = Stack.top();
24
Stack.pop();
if (visited[v] == false)
gr.DFSUtil(v, visited);
int main()
Graph g(5);
g.addEdge(1, 0);
g.addEdge(0, 2);
g.addEdge(2, 1);
g.addEdge(0, 3);
g.addEdge(3, 4);
g.printSCCs();
return 0;
25
Program No. 8
Write a program to implement file compression (and un-compression) using Huffman‘s algorithm.
// Initializing heap
struct Min_Heap* Min_Heap
= (struct Min_Heap*)malloc(sizeof(struct Min_Heap));
Min_Heap->size = unique_size;
Min_Heap->array = (struct Node**)malloc(
Min_Heap->size * sizeof(struct Node*));
// newNode is a function
// to initialize new node
Min_Heap->array[i] = newNode(arr[i], freq[i]);
}
26
int n = Min_Heap->size - 1;
for (i = (n - 1) / 2; i >= 0; --i) {
return Min_Heap;
}
if (root->r) {
t[top] = 1;
printCodesIntoFile(fd2, root->r, t, top + 1);
}
if (isLeaf(root)) {
data = (code*)malloc(sizeof(code));
tree = (Tree*)malloc(sizeof(Tree));
data->p = NULL;
data->k = root->character;
tree->g = root->character;
write(fd2, &tree->g, sizeof(char));
for (i = 0; i < top; i++) {
data->code_arr[i] = t[i];
}
tree->len = top;
write(fd2, &tree->len, sizeof(int));
tree->dec
= convertBinaryToDecimal(data->code_arr, top);
27
write(fd2, &tree->dec, sizeof(int));
data->l = top;
data->p = NULL;
if (k == 0) {
front = rear = data;
k++;
}
else {
rear->p = data;
rear = rear->p;
}
}
}
30
Program No. 9 & 10
#include<stdio.h>
#include<string.h>
intfindMax(int n1,int n2){
if(n1>n2){
return n1;
}else{
return n2;
}
}
intknapsack(int W,int wt[],int val[],int n){
int K[n+1][W+1];
for(int i =0; i<=n; i++){
for(int w =0; w<=W; w++){
if(i ==0|| w ==0){
K[i][w]=0;
}elseif(wt[i-1]<= w){
K[i][w]=findMax(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];
}
intmain(){
int val[5]={70,20,50};
int wt[5]={11,12,13};/* A Naive recursive implementation
of 0-1 Knapsack problem */
#include <stdio.h>
31
// 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;
// Driver code
int main()
{
int profit[] = { 60, 100, 120 };
int weight[] = { 10, 20, 30 };
int W = 50;
int n = sizeof(profit) / sizeof(profit[0]);
printf("%d", knapSack(W, weight, profit, n));
return 0;
}int W =30;
int len =sizeof val /sizeof val[0];
printf("Maximum Profit achieved with this knapsack: %d",knapsack(W, wt, val,
len));
Output
Maximum Profit achieved with this knapsack: 120
32
// 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;
// Driver code
int main()
{
int profit[] = { 60, 100, 120 };
int weight[] = { 10, 20, 30 };
int W = 50;
int n = sizeof(profit) / sizeof(profit[0]);
printf("%d", knapSack(W, weight, profit, n));
return 0;
}
Output
220
220
#include <stdio.h>
#include <conio.h>
#define max 10
Advertisement
33
int w[max],i,j,p[max];
int n,m;
float unit[max];
int y[max],x[max],fp=-1,fw;
void get()
{
printf(“\n Enter total number of items: “);
scanf(“%d”,&n);
printf(“\n Enter the Maximum capacity of the Sack: “);
scanf(“%d”,&m);
for(i=0;i<n;i++)
{
printf(“\n Enter the weight of the item # %d : “,i+1);
scanf(“%d”,&w[i]);
printf(“\n Enter the profit of the item # %d : “, i+1);
scanf(“%d”, &p[i]);
}
}
Adv ert is em ents
REPORT THIS AD
void show()
{
float s=0.0;
printf(“\n\tItem\tWeight\tCost\tUnit Profit\tSelected “);
for(i=0;i<n;i++)
printf(“\n\t%d\t%d\t%d\t%f\t%d”,i+1,w[i],p[i],unit[i],x[i]);
printf(“\n\n The Sack now holds following items : “);
for(i=0;i<n;i++)
if(x[i]==1)
{
printf(“%d\t”,i+1);
s += (float) p[i] * (float) x[i];
}
printf(“\n Maximum Profit: %f “,s);
}
/*Arrange the item based on high profit per Unit*/
void sort()
{
Adv ert is em ents
REPORT THIS AD
int t,t1;
float t2;
for(i=0;i<n;i++)
unit[i] = (float) p[i] / (float) w[i];
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
34
{
if(unit[i] < unit[j])
{
t2 = unit[i];
unit[i] = unit[j];
unit[j] = t2;
t = p[i];
p[i] = p[j];
p[j] = t;
t1 = w[i];
w[i] = w[j];
Adv ert is em ents
REPORT THIS AD
w[j] =t1;
}
}
}
}
float bound(float cp,float cw,int k)
{
float b = cp;
float c = cw;
for(i=k;i<=n;i++)
{
c = c+w[i];
if( c < m)
b = b +p[i];
else
return (b+(1-(c-m)/ (float)w[i])*p[i]);
}
return b;
Adv ert is em ents
REPORT THIS AD
}
void knapsack(int k,float cp,float cw)
{
if(cw+w[k] <= m)
{
y[k] = 1;
if(k <= n)
knapsack(k+1,cp+p[k],cw+w[k]);
if(((cp+p[k]) > fp) && ( k == n))
{
fp = cp+p[k];
fw = cw+w[k];
for(j=0;j<=k;j++)
x[j] = y[j];
35
}
}
if(bound(cp,cw,k) >= fp)
{
Adv ert is em ents
REPORT THIS AD
y[k] = 0;
if( k <= n)
knapsack(k+1,cp,cw);
if((cp > fp) && (k == n))
{
fp = cp;
fw = cw;
for(j=0;j<=k;j++)
x[j] = y[j];
}
}
}
void main()
{
clrscr();
printf(“\n\n\n\t\t ******** KNAPSACK PROBLEM ********”);
printf(“\n\t\t —————————————–“);
get();
Adv ert is em ents
REPORT THIS AD
#include <iostream>
36
#include <algorithm>
#include <vector>
#include <queue>
class Item {
public:
int value;
int weight;
double ratio;
class KnapsackNode {
public:
vector<int> items;
int value;
int weight;
class Knapsack {
public:
int maxWeight;
vector<Item> items;
int solve() {
sort(this->items.begin(), this->items.end(), [](const Item& a, const Item& b) {
return a.ratio > b.ratio;
});
37
int bestValue = 0;
queue<KnapsackNode> q;
q.push(KnapsackNode({}, 0, 0));
while (!q.empty()) {
KnapsackNode node = q.front();
q.pop();
int i = node.items.size();
if (i == this->items.size()) {
bestValue = max(bestValue, node.value);
} else {
Item item = this->items[i];
KnapsackNode withItem(node.items, node.value + item.value,
node.weight + item.weight);
if (isPromising(withItem, this->maxWeight, bestValue)) {
q.push(withItem);
}
KnapsackNode withoutItem(node.items, node.value,
node.weight);
if (isPromising(withoutItem, this->maxWeight, bestValue)) {
q.push(withoutItem);
}
}
}
return bestValue;
}
38
bound += remainingWeight * item.ratio;
break;
}
}
return bound;
}
};
output:
Best value: 220
39
Program No.11
Write a program that uses dynamic programming algorithm to solve the optimal binary
search tree problem.
// A naive recursive implementation of
// optimal binary search tree problem
#include <bits/stdc++.h>
using namespace std;
40
{
// Here array keys[] is assumed to be
// sorted in increasing order. If keys[]
// is not sorted, then add code to sort
// keys, and rearrange freq[] accordingly.
return optCost(freq, 0, n - 1);
}
// Driver Code
int main()
{
int keys[] = {10, 12, 20};
int freq[] = {34, 8, 50};
int n = sizeof(keys) / sizeof(keys[0]);
cout << "Cost of Optimal BST is "
<< optimalSearchTree(keys, freq, n);
return 0;
}
Output
Cost of Optimal
Optimal solution is 142
41