0% found this document useful (0 votes)
475 views

NIEIT Ada Lab Manual

The document provides information about various algorithms and their analysis. It discusses linear search, binary search, heap sort, and merge sort algorithms. It describes the time complexity of each algorithm and provides pseudo code for their implementation. It also instructs the reader to implement and time linear search, binary search, heap sort, and merge sort algorithms for different input sizes (n) and to plot a graph of the time taken versus n.
Copyright
© Attribution Non-Commercial (BY-NC)
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)
475 views

NIEIT Ada Lab Manual

The document provides information about various algorithms and their analysis. It discusses linear search, binary search, heap sort, and merge sort algorithms. It describes the time complexity of each algorithm and provides pseudo code for their implementation. It also instructs the reader to implement and time linear search, binary search, heap sort, and merge sort algorithms for different input sizes (n) and to plot a graph of the time taken versus n.
Copyright
© Attribution Non-Commercial (BY-NC)
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/ 39

NIE Institute of

Technology
Department of Computer Science & Engg
And
Department of Information science & Engg

ALGORITHMS LABORATORY
th MANUAL
For 5 SEM CSE & ISE
Sub Code: 06CSL58

Introduction

Analysis of algorithms

Analysis is one of the important step in problem solving. Here we estimate time and
space for a given problem. Once we have these estimations, select an algorithm which is more
efficient in terms of time and space.

Time Efficiency: Indicates how fast the algorithm runs.

Space Efficiency: Indicates how much extra memory is required for the algorithm .

The most important characteristics of any algorithm are it’s simplicity and generality.

Simplicity: Simple algorithms are easy to understand, debug and easy to program.
Generality: It should be a generic solution to the given problem.

Design an algorithm

After the problem is clearly stated and the type of algorithm and data structure are
selected, the solution to solve the problem should be specified. One or more algorithms may be
correct to solve a given problem. But effectiveness may be different from solution to solution.

Algorithm

An algorithm is a technique to solve a problem systematically. It takes a set of input and


produces a desired output. Now algorithm can be defined as an unambiguous an ordered
sequence of steps that are carried to get a solution to a problem. Pictorial representation of an
algorithm is as shown below .

Notion of an algorithm

Linear Search
In computer science, linear search is a search algorithm, also known as sequential
search, that is suitable for searching a list of data for a particular value.

It operates by checking every element of a list one at a time in sequence until a match is
found. Linear search runs in O(n). If the data are distributed randomly, the expected number of
comparisons that will be necessary is: where n is the number of elements in the list and k is the
number of times that the value being searched for appears in the list. The best case is that the
value is equal to the first element tested, in which case only 1 comparison is needed. The worst
case is that the value is not in the list (or it appears only once at the end of the list), in which case
n comparisons are needed.

The simplicity of the linear search means that if just a few elements are to be searched it
is less trouble than more complex methods that require preparation such as sorting the list to be
searched or more complex data structures, especially when entries may be subject to frequent
revision. Another possibility is when certain values are much more likely to be searched for than
others and it can be arranged that such values will be amongst the first considered in the list.

The following pseudo code describes the linear search technique.


For each item in the list:
Check to see if the item you're looking for matches the item in the list.
If it matches.
Return the location where you found it (the index).
If it does not match.
Continue searching until you reach the end of the list.
If we get here, we know the item does not exist in the list. Return -1.

Binary Search Algorithm (or binary chop)

Binary search is a technique for locating a particular value in a sorted list. The method
makes progressively better guesses, and closes in on the location of the sought value by selecting
the middle element in the span (which, because the list is in sorted order, is the median value),
comparing its value to the target value, and determining if it is greater than, less than, or equal to
the target value. A guessed index whose value turns out to be too high becomes the new upper
bound of the span, and if its value is too low that index becomes the new lower bound. A binary
search is an example of a divide and conquer search algorithm.

The following pseudo code describes the Binary search technique

BinarySearch(A[0..N-1], value, low, high)


{
if (high < low)
return -1 // not found
mid = low + ((high - low) / 2) // Note: not (low + high) / 2 !!
if (A[mid] > value)
return BinarySearch(A, value, low, mid-1)
else if (A[mid] < value)
return BinarySearch(A, value, mid+1, high)
else
return mid // found
}

• Implement Recursive Binary search and Linear search and determine the
time required to search an element. Repeat the experiment for
different values of n, the number of elements in the list to be
searched and plot a graph of the time taken versus n.
Heap Sort

Heapsort (method) is a comparison-based sorting algorithm, and is part of the selection


sort family. The heap sort works as its name suggests - it begins by building a heap out of the
data set, and then removing the largest item and placing it at the end of the sorted array. After
removing the largest item, it reconstructs the heap and removes the largest remaining item and
places it in the next open position from the end of the sorted array. This is repeated until there are
no items left in the heap and the sorted array is full. Elementary implementations require two
arrays - one to hold the heap and the other to hold the sorted elements.
Heapsort uses two heap operations: insertion and root deletion. Each extraction places an
element in the last empty location of the array. The remaining prefix of the array stores the
unsorted elements.

• Sort a given set of elements using the Heap sort method and determine
the time required to sort the elements. Repeat the experiment for
different values of n, the number of elements in the list to be
sorted and plot a graph of the time taken versus n.

#include<iostream.h>
#include<conio.h>
//#define MTIMES 100
//#define NTIMES 10000
#include<time.h>
#include<dos.h>
#include<stdlib.h>
int i,n,a[25000],j;

class heap
{
public: void heapify(int a[], int n);
void heapsort(int a[],int n);

};

void heap::heapify(int a[],int n)


{
int i,j,v,heap,k;
for(i=n/2;i>0;i--)
{
k=i;
v=a[i];
heap=0;
while(!heap&&(2*k)<=n)
{
j=2*k;
if(j<n)
{
if(a[j]<a[j+1])
{
j=j+1;
}
}
if(v>=a[j])
heap=1;
else
{
a[k]=a[j];
k=j;
}
}
a[k]=v;
}
}

void heap::heapsort(int a[],int n)


{
int temp,i,j;
heapify(a,n);
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
if(a[i]>a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
}

void main()
{

clock_t st, et;


heap h;
clrscr();
cout<<"Enter the value of N:\n"<<endl;
cin>>n;
cout<<"Enter the array element:\n"<<endl;
for(i=1;i<=n;i++)
//cin>>a[i];
a[i]=rand()%100;
cout<<"The array element genearted are:\n"<<endl;
for(i=1;i<=n;i++)
cout<<"\t"<<a[i];

st=clock();
// for(i=0;i<MTIMES;i++)
//for(j=0;j<NTIMES;j++)
h.heapsort(a,n);
et=clock();

cout<<"\nThe sorted elements are:\n"<<endl;


for(i=1;i<=n;i++)
cout<<"\t"<<a[i];

cout<<"\nBegin time="<<st<<endl;
cout<<"End time="<<et<<endl;
cout<<"Total number of clock ticks="<<(et-st)<<endl;
cout<<"Total time required="<<(et-st)/CLK_TCK<<endl;
getch();
}
Merge sort

Merge sort is an O(n log n) comparison-based sorting algorithm. In most implementations it is


stable, meaning that it preserves the input order of equal elements in the sorted output. It is an
example of the divide and conquer algorithmic paradigm. It was invented by John von Neumann
in 1945.

Conceptually, a merge sort works as follows:

• If the list is of length 0 or 1, then it is already sorted. Otherwise:

• Divide the unsorted list into two sublists of about half the size.

• Sort each sublist recursively by re-applying merge sort.

• Merge the two sublists back into one sorted list.

Merge sort incorporates two main ideas to improve its runtime:

• A small list will take fewer steps to sort than a large list.

• Fewer steps are required to construct a sorted list from two sorted lists than two unsorted
lists. For example, you only have to traverse each list once if they're already sorted (see
the merge function below for an example implementation).

Example: Using merge sort to sort a list of integers contained in an array:


Suppose we have an array A with n indices ranging from A0 to An − 1. We apply merge sort to
A(A0..Ac − 1) and A(Ac..An − 1) where c is the integer part of n / 2. When the two halves are
returned they will have been sorted. They can now be merged together to form a sorted array.
• Sort a given set of elements using Merge sort method and determine the
time required to sort the elements. Repeat the experiment for different
values of n, the number of elements in the list to be sorted and plot a
graph of the time taken versus n.

#include<iostream.h>
#include<conio.h>
#include<dos.h>
#include<time.h>
int a[1000],b[1000],c[1000],i,n;

class msort
{

public: void merge(int,int,int);


void mergesort(int,int);
};

void msort::merge(int l,int h,int mid)


{
int i=l,j=mid+1,k=l;
while(i<=mid && j<=h)
{
if(a[i]<a[j])
c[k++]=a[i++];
else
c[k++]=a[j++];
}
while(i<=mid)
c[k++]=a[i++];
while(j<=h)
c[k++]=a[j++];
for(i=l;i<k;i++)
a[i]=c[i];
}

void msort::mergesort(int l,int h)


{

int mid;
if(l<h)
{
mid=(l+h)/2;
delay(100);
mergesort(l,mid);
mergesort(mid+1,h);
merge(l,h,mid);
}
}
void main()
{
clock_t st,et;

msort m;
clrscr();

cout<<"Enter the value of n"<<endl;


cin>>n;
cout<<"Enter the elements"<<endl;
for(i=0;i<n;i++)
cin>>a[i];
st=clock();
m.mergesort(0,n-1);
et=clock();

cout<<"\nThe sorted array is"<<endl;


for(i=0;i<n;i++)
cout<<"\t"<<a[i];

cout<<"\nBegin time="<<st<<endl;
cout<<"End time="<<et<<endl;
cout<<"Total number of clock ticks="<<(et-st)<<endl;
cout<<"Total time required="<<(et-st)/CLK_TCK<<endl;
getch();
}
Selection sort

Selection sort is a sorting algorithm, specifically an in-place comparison sort. It has O(n2)
complexity, making it inefficient on large lists, and generally performs worse than the similar
insertion sort. Selection sort is noted for its simplicity, and also has performance advantages over
more complicated algorithms in certain situations.

The algorithm works as follows:

• Find the minimum value in the list

• Swap it with the value in the first position

• Repeat the steps above for the remainder of the list (starting at the second position and
advancing each time)

Effectively, we divide the list into two parts: the sublist of items already sorted, which we build
up from left to right and is found at the beginning, and the sublist of items remaining to be
sorted, occupying the remainder of the array.
• a Sort a given set of elements using Selection sort and determine the
time required to sort elements. Repeat the experiment for different
values of n, the number of elements in the list to be sorted and plot
a graph of the time taken versus n.

#include<iostream.h>
#include<conio.h>
#include<dos.h>
#include<time.h>
#include<stdlib.h>

int a[16000],i,n,j,min,t;

class selsort
{
public: void selection(int);
};

void selsort:: selection(int n)


{
for(i=0;i<n-1;i++)
{
min=i;

for(j=i+1;j<n;j++)
{
if(a[j]<a[min])
min=j;
}
t=a[i];
a[i]=a[min];
a[min]=t;
}
}

void main()
{
clock_t st,et;
selsort s;
clrscr();

cout<<"Enter the value of n"<<endl;


cin>>n;
cout<<"The elements generated are"<<endl;
for(i=0;i<n;i++)
a[i]=rand()%100;

for(i=0;i<n;i++)
cout<<"\t"<<a[i];

st=clock();
s.selection(n);
et=clock();
cout<<"\nThe sorted array is"<<endl;
for(i=0;i<n;i++)
cout<<"\t"<<a[i];

cout<<"\nBegin time="<<st<<endl;
cout<<"End time="<<et<<endl;
cout<<"Total number of clock ticks="<<(et-st)<<endl;
cout<<"Total time required="<<(et-st)/CLK_TCK<<endl;
getch();
}
The knapsack problem or rucksack problem

The knap sack problem is a problem in combinatorial optimization: Given a set of items,
each with a weight and a value, determine the number of each item to include in a collection so
that the total weight is less than a given limit and the total value is as large as possible. It derives
its name from the problem faced by someone who is constrained by a fixed-size knapsack and
must fill it with the most useful items.

The problem often arises in resource allocation with financial constraints. A similar
problem also appears in combinatorics, complexity theory, cryptography and applied
mathematics.

0-1 knapsack problem

A dynamic programming solution for the 0-1 knapsack problem also runs in pseudo-
polynomial time. Assume w1, ..., wn, W are strictly positive integers. Define A(j, Y) to be the
maximum value that can be attained with weight less than or equal to Y using items up to j.

We can define A(j,Y) recursively as follows:

• A(0, Y) = 0

• A(j, 0) = 0

• A(j, Y) = A(j − 1, Y)  if wj > Y

• A(j, Y) = max { A(j − 1, Y),  pj + A(j − 1, Y − wj) }  if wj ≤ Y.

The solution can then be found by calculating A(n, W). To do this efficiently we can use a table
to store previous computations. This solution will therefore run in O(nW) time and O(nW) space,
though with some slight modifications we can reduce the space complexity to O(W).

• Implement 0/1 Knapsack problem using dynamic programming


#include<iostream.h>
#include<conio.h>

int n,m,i,j,a,b,w[10],v[10][10],p[10];

class knap
{
public: int max(int a,int b);
void ksack(int n,int w[],int m,int v[][10],int p[]);
void optsol(int n,int m,int w[],int v[10][10]);
};

int knap::max(int a,int b)


{
return a>b?a:b;
}

void knap::ksack(int n,int w[],int m,int v[][10],int p[])


{
int i,j;
for(i=0;i<=n;i++)
{
for(j=0;j<=m;j++)
{
if(i==0||j==0)
v[i][j]=0;
else if(j<w[i])
v[i][j]=v[i-1][j];
else
v[i][j]=max(v[i-1][j],v[i-1][j-w[i]]+p[i]);
}
}
}
void knap::optsol(int n,int m,int w[],int v[10][10])
{
int i,j,x[10];

cout<<"\nThe optimal solution is"<<v[n][m]<<endl;


for(i=0;i<n;i++)
x[i]=0;

i=n; j=m;
while(i!=0 && j!=0)
{
if(v[i][j]!=v[i-1][j])
{
x[i]=1;
j=j-w[i];
}
i=i-1;
}
cout<<"\nThe Object Selected is:\n";
for(i=1;i<=n;i++)
{
cout<<"X["<<i<<"]"<<"\t";
}
cout<<"=";

for(i=1;i<=n;i++)
{
cout<<x[i];
}
}

void main()
{
knap k;
clrscr();
cout<<"Enter the number of objects\n";
cin>>n;
cout<<"\nEnter the weights of n objects\n";
for(i=1;i<=n;i++)
cin>>w[i];
cout<<"\nEnter the profits of n objects\n";
for(i=1;i<=n;i++)
cin>>p[i];
cout<<"\nEnter the capacity of knapsack\n";
cin>>m;
k.ksack(n,w,m,v,p);
k.optsol(n,m,w,v);
getch();
}
Dijkstra's algorithm,

Dijkstra’s algorithm conceived by Dutch computer scientist Edsger Dijkstra in 1959 is a


graph search algorithm that solves the single-source shortest path problem for a graph with
nonnegative edge path costs, producing a shortest path tree. This algorithm is often used in
routing.

For a given source vertex (node) in the graph, the algorithm finds the path with lowest
cost (i.e. the shortest path) between that vertex and every other vertex. It can also be used for
finding costs of shortest paths from a single vertex to a single destination vertex by stopping the
algorithm once the shortest path to the destination vertex has been determined. For example, if
the vertices of the graph represent cities and edge path costs represent driving distances between
pairs of cities connected by a direct road, Dijkstra's algorithm can be used to find the shortest
route between one city and all other cities. As a result, the shortest path first is widely used in
network routing protocols, most notably (Intermediate system to intermediate system (IS-IS),
and OSPF (Open Shortest Path First).
• From a given vertex in a weighted connected graph, find shortest
paths to other vertices using Dijkstra's algorithm

#include<iostream.h>
#include<conio.h>

int i,j,count=1,source,curr,cost[10][10];
int visit[100],d[100],n;

class dijstra
{
public: void read();
int min(int a,int b);
int getcur();
void SSPT();
};

int dijstra::min(int a,int b)


{
if(a<=b)
return a;
else
return b;
}

int dijstra::getcur()
{
int i=1,curr;
while(visit[i]==1)
i++;
curr=i;
for(i=curr+1;i<=n;i++)
if(d[i]<d[curr] && visit[i]==0)
curr=i;
return curr;
}

void dijstra::read()
{
cout<<"\n Enter the number of nodes:";
cin>>n;
cout<<"\n Enter the cost matrix:\n";
cout<<"\nEnter 0->diagonal elements and 99->No direct edge cost\n";
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cin>>cost[i][j];
visit[i]=0;
}
}
cout<<"\n Enter the source vertex:\n";
cin>>source;
visit[source]=1;
}

void dijstra::SSPT()
{
for(i=1;i<=n;i++)
d[i]=cost[source][i];

while(count<n)
{
curr=getcur();
visit[curr]=1;
count++;
for(i=1;i<=n;i++)
if(visit[i]==0)
{
d[i]=min(d[i],d[curr]+cost[curr][i]);
}
}
cout<<"\n PATH DISTANCE\n";
for(i=1;i<=n;i++)
cout<<"\n("<<source<<","<<i<<")"<<"--->"<<d[i];
}

void main()
{
dijstra d;
clrscr();
d.read();
d.SSPT();
getch();
}

/*--------------------------------------------------------------------
output:

Enter the number of nodes: 3


Enter the cost matrix:
Enter 0->diagonal elements and 99->No direct edge cost:
0 10 30
20 0 15
20 5 0
Enter the source vertex: 1
PATH DISTANCE
(1,1)---------> 0
(1,2)---------> 10
(1,3)---------> 25
----------------------------------------------------------------------*/

Quick Sort
Quicksort is a well-known sorting algorithm developed by C. A. R. Hoare that, on
average, makes Θ(nlogn) (big O notation) comparisons to sort n items. However, in the worst
case, it makes Θ(n2) comparisons. Typically, quicksort is significantly faster in practice than
other Θ(nlogn) algorithms, because its inner loop can be efficiently implemented on most
architectures, and in most real-world data, it is possible to make design choices which minimize
the probability of requiring quadratic time.

Quicksort is a comparison sort and, in efficient implementations, is not a stable sort.


Quicksort sorts by employing a divide and conquer strategy to divide a list into two sub-lists.
Full example of quicksort on a random set of numbers. The boxed element is the pivot. It is
always chosen as the last element of the partition.

The steps are:

• Pick an element, called a pivot, from the list.

• Reorder the list so that all elements which are less than the pivot come before the pivot
and so that all elements greater than the pivot come after it (equal values can go either
way). After this partitioning, the pivot is in its final position. This is called the partition
operation.

• Recursively sort the sub-list of lesser elements and the sub-list of greater elements.

The base case of the recursion are lists of size zero or one, which are always sorted. In simple
pseudocode, the algorithm might be expressed as this:
function quicksort(array)
var list less, greater
if length(array) ≤ 1
return array
select and remove a pivot value pivot from array
for each x in array
if x ≤ pivot then append x to less
else append x to greater
return concatenate(quicksort(less), pivot, quicksort(greater))

• Sort a given set of elements using Quick sort method and determine
the time required sort the elements. Repeat the experiment for
different values of n, the number of elements in the list to be
sorted and plot a graph of the time taken versus n.
#include<iostream.h>
#include<conio.h>
#include<time.h>
#include<stdlib.h>

class quicksort
{
int n,a[1000];
int partition(int,int);
void swap(int,int);
public:
void qsort(int,int);
void getarray();
void putarray();
quicksort(int i)
{ n=i;
}
};
int quicksort::partition(int low,int high)
{
int p,i,j;
p=i=low;
j=high+1;
while(i<=j)
{ do
i++;
while(a[i]<a[p]);

do
j--;
while(a[j]>a[p]);
if(i<j)
swap(i,j);
}
swap(p,j);
return j;
}

void quicksort::qsort(int low,int high)


{
int mid;
if(low<high)
{ mid=partition(low,high);
qsort(low,mid-1);
qsort(mid+1,high);
}
}

void quicksort::getarray()
{
for(int i=0;i<n;i++)
cin>>a[i];
}
void quicksort::putarray()
{
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
}

void quicksort::swap(int i,int j)


{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}

void main()
{
int n;
clock_t s,e;
clrscr();
cout<<"\nEnter the no. of elements:\n ";
cin>>n;
s=clock();
quicksort a1(n);
cout<<"\nEnter the elements of the array:\n";
a1.getarray();
cout<<"\nEntered elements of the array:\n";
a1.putarray();
cout<<"\nThe sorted array is:\n";
a1.qsort(0,n-1);
e=clock();
a1.putarray();
cout<<"\nTime taken for sort\n"<<(e-s)/CLK_TCK;
getch();
}

Kruskal’s Algorithm

Kruskal's algorithm is an algorithm in graph theory that finds a minimum spanning tree for a
connected weighted graph. This means it finds a subset of the edges that forms a tree that
includes every vertex, where the total weight of all the edges in the tree is minimized. If the
graph is not connected, then it finds a minimum spanning forest (a minimum spanning tree for
each connected component). Kruskal's algorithm is an example of a greedy algorithm. This
algorithm first appeared in Proceedings of the American Mathematical Society, pp. 48–50 in
1956, and was written by Joseph Kruskal

It works as follows:

• create a forest F (a set of trees), where each vertex in the graph is a separate tree
• create a set S containing all the edges in the graph

• while S is nonempty

• remove an edge with minimum weight from S

• if that edge connects two different trees, then add it to the forest, combining two
trees into a single tree

• otherwise discard that edge.

At the termination of the algorithm, the forest has only one component and forms a minimum
spanning tree of the graph.

• Find Minimum Cost Spanning Tree of a given undirected graph using


Kruskal’s algorithm.

#include<iostream.h>
#include<conio.h>

int cost[10][10],root[10],min,edge=1,mincost=0;
int i,j,a,b,u,v,n;

class MCST
{
public: void kruskal();
};

void MCST::kruskal()
{
cout<<"Spanning Trees is:\n";
while(edge<n)
{
for(i=1,min=999;i<=n;i++)
for(j=1;j<=n;j++)
if(cost[i][j]<min)
{
min=cost[i][j];
a=u=i;
b=v=j;
}
while(root[u])
u=root[u];
while(root[v])
v=root[v];

if(u!=v)
{
edge++;
cout<<"\n"<<"("<<a<<","<<b<<")"<<"="<<min;
mincost+=min;
root[v]=u;
}
cost[a][b]=cost[b][a]=999;
}
cout<<"\n Minimum cost = "<<mincost;
}

void main()
{

MCST k;
clrscr();
cout<<"\n Enter the number of vertices:";
cin>>n;
cout<<"\n Enter the cost matrix";
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
cin>>cost[i][j];
if(cost[i][j]==0)
cost[i][j]=999;
}
k.kruskal();
getch();
}

Breadth-first search (BFS)


In graph theory, Breadth-First Search (BFS) is a graph search algorithm that begins at
the root node and explores all the neighboring nodes. Then for each of those nearest nodes, it
explores their unexplored neighbor nodes, and so on, until it finds the goal.BFS is an uninformed
search method that aims to expand and examine all nodes of a graph or combination of
sequences by systematically searching through every solution. In other words, it exhaustively
searches the entire graph or sequence without considering the goal until it finds it. It does not use
a heuristic algorithm.From the standpoint of the algorithm, all child nodes obtained by expanding
a node are added to a FIFO queue.

Algorithm (informal)
• Enqueue the root node.

• Dequeue a node and examine it.

• If the element sought is found in this node, quit the search and return a result.
• Otherwise enqueue any successors (the direct child nodes) that have not yet been
examined.

• If the queue is empty, every node on the graph has been examined – quit the search and
return "not found".

• Repeat from Step 2.

Note: Using a stack instead of a queue would turn this algorithm into a depth-first search. (DFS)

Applications
Breadth-first search can be used to solve many problems in graph theory, for example:

• Finding all connected components in a graph.

• Finding all nodes within one connected component

• Copying Collection, Cheney's algorithm

• Finding the shortest path between two nodes u and v (in an unweighted graph)

• Finding the shortest path between two nodes u and v (in a weighted graph: see talk page)

• Testing a graph for bipartiteness

• (Reverse) Cuthill–McKee mesh numbering

• a. Print all the nodes reachable from a given starting node in a


digraph using BFS method.

#include<iostream.h>
#include<conio.h>

int i,j,n,f=0,r=-1,source;
int v,cost[10][10],q[10],q1[10];

class reach
{
public: void bfs(int u);
void read();
};

void reach::bfs(int u)
{
for(v=1;v<=n;v++)
{
if(cost[u][v]==1&&q1[v]==0)
q[++r]=v;
}
if(f<=r)
{
q1[q[f]]=1;
bfs(q[f++]);
}
}
void reach::read()
{
cout<<"Enter the number of vertices\n";
cin>>n;
cout<<"Enter the cost adjacency matrix\n";
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>cost[i][j];
for(i=1;i<=n;i++)
{
q[i]=0;
q1[i]=0;
}
cout<<"Enter the source vertex\n";
cin>>source;
bfs(source);
cout<<"The vertices from :"<<source<<"\n\n";
for(i=1;i<=n;i++)
{
if(q1[i]==1)
cout<<i<<"\tis reachable\n";
}
}
void main()
{
reach r;
clrscr();
r.read();
getch();
}
Depth-first search (DFS)

Depth First Search is an algorithm for traversing or searching a tree, tree structure, or
graph. One starts at the root (selecting some node as the root in the graph case) and explores as
far as possible along each branch before backtracking.

Applications
Algorithms where DFS is used:

• Finding connected components.


• Topological sorting.

• Finding 2-(edge or vertex)-connected components.

• Finding strongly connected components.

• Solving puzzles with only one solution, such as mazes.


b. Check whether a given graph is connected or not using DFS method.
#include<iostream.h>
#include<conio.h>

int n,j,source,cost[10][10],s[10],i,connected,flag;
class graph
{
public: void dfs(int n,int u,int cost[10][10],int s[10]);
void read();
};

void graph::dfs(int n,int u,int cost[10][10],int s[10])


{
int v;
s[u]=1;
for(v=1;v<=n;v++)
if(cost[u][v]==1&&!s[v])
dfs(n,v,cost,s);
}

void graph::read()
{
cout<<"Enter the number of vertices\n";
cin>>n;
cout<<"Enter the cost matrix\n";
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>cost[i][j];
connected=0;
for(j=1;j<=n;j++)
{
for(i=1;i<=n;i++)
s[i]=0;
dfs(n,j,cost,s);
flag=0;
for(i=1;i<=n;i++)
if(s[i]==0)
flag=1;
if(flag==0)
connected=1;
}
if(connected==1)
cout<<"The graph is connected\n";
else
cout<<"the graph is not connected\n";
}

void main()
{
graph g;
clrscr();
g.read();
getch();
}

Subset construction
Backtracking is a refinement of the brute force approach, which systematically searches
for a solution to a problem among all available options. It does so by assuming that the solutions
are represented by vectors (v1, ..., vm) of values and by traversing, in a depth first manner, the
domains of the vectors until the solutions are found.

The objective of this problem is to find as many subsets as possible whose individual sum
equals some constant value.
• Find a subset of a given set S = {sl,s2,.....,sn} of n positive
integers whose sum is equal to a given positive integer d. For
example, if S= {1, 2, 5, 6, 8} and d = 9 there are two
solutions{1,2,6}and{1,8}.A suitable message is to be displayed if
the given problem instance doesn't have a solution.

#include<iostream.h>
#include<conio.h>
#include<stdlib.h>

int s[10],d,n,visit[10],i,k,r=0, flag=0;


class set
{
public: void subset(int sum,int i,int r);
void read();
};

void set::subset(int sum,int i,int r)


{
int j;
visit[i]=1;
if(sum+s[i]==d)
{
flag=1;
cout<<"\n Subset "<<++k<<": ";
for(j=0;j<=i;j++)
if(visit[j])
cout<<s[j]<<"\t";
}
else if(sum+s[i]+s[i+1]<=d)
subset(sum+s[i],i+1,r-s[i]);
if((sum+r-s[i]>=d) && (sum+s[i+1]<=d))
{
visit[i]=0;
subset(sum,i+1,r-s[i]);
}
}

void set::read()
{
cout<<"\n Enter the number of elements:\n";
cin>>n;
cout<<"\n Enter the elements in ascending order:";
for(i=0;i<n;i++)
{
cout<<"\n\t:";
cin>>s[i];
}
cout<<"\n Enter the sum:";
cin>>d;
for(i=0;i<n;i++)
r+=s[i];
subset(0,0,r);
if(!flag)
cout<<"Solution doesnot exist:\n";

void main()
{
set s;
clrscr();
s.read();
getch();
}

******************************************************************************
*
/* OUTPUT
Enter the number of elements:4

Enter the elements:


:3

:5

:6

:7

Enter the sum:15

Subset 1: 3 5 7

*/
******************************************************************************
*

The Boyer–Moore–Horspool algorithm or Horspool's algorithm

Is an algorithm for finding substrings in strings. It is a simplification of the Boyer-Moore


algorithm which is related to the Knuth-Morris-Pratt algorithm. The algorithm trades space for
time in order to obtain an average-case complexity of O(N) on random text, although it has
O(MN) in the worst case. The length of the pattern is M and the length of the search string is N.

• a. Implement Horspool algorithm for String Matching.

#include<iostream.h>
#include<conio.h>
#include<string.h>
#include<stdio.h>

int table[256],flag;
char t[100],p[100];

class horse
{
public: void shift_table(char p[],int m);
int horspool(char t[],char p[]);
void read();
};

void horse::shift_table(char p[],int m)


{
int i,j;
for(i=0;i<127;i++)
table[i]=m;
for(j=0;j<m-1;j++)
table[p[j]]=m-1-j;
}

int horse::horspool(char t[],char p[])


{
int i,k,m,n;
m=strlen(p);
n=strlen(t);
shift_table(p,m);
i=m-1;
while(i<=n-1)
{
k=0;
while(k<=m-1 && p[m-1-k]==t[i-k])
k++;
if(k==m)
return i-m+1;
else
i+=table[t[i]];
}
return -1;
}

void horse::read()
{
cout<<"\n Enter the text:\n\t";
gets(t);
cout<<"\n Enter the pattern:\n\t";
gets(p);

flag=horspool(t,p);

if(flag==-1)
cout<<"\n Pattern string not found";
else
cout<<"\n Pattern string found at position= "<<flag+1<<"\n";
getch();
}

void main()
{
clrscr();
horse h;
h.read();
}
******************************************************************************
/* OUTPUT
Run 1:
Enter the text:
ferrari rules

Enter the pattern:


fiat
No match

Run 2:
Enter the text:
jim_saw_me_in_the_barber_shop

Enter the pattern:


barber
Match found at position 19
*/
******************************************************************************
*

Binomial coefficient
The Binomial coefficients C(n, k) is the number of ways of choosing a subset of k
elements from a set of n elements. It arises in probability and statistics, e.g., the
probability of flipping a biased coin (with probability p of heads) n times and getting
exactly k heads is C(n, k) pk (1-p)k. One formula for computing binomial coefficients is
C(n, k) = n! / (k! (n-k)!). This formula is not so amenable to direct computation because
the intermediate results may overflow, even if the final answer does not. For example
C(100, 15) = 253338471349988640 fits in a 64-bit long, but the binary representation
of 100! is 525 bits long. Pascal's identity expresses C(n, k) in terms of smaller binomial
coefficients:

To understand why Pascal's identity is valid, consider some arbitrary element x. To


choose k of n elements, we either select element x (in which case we still need to choose
k-1 of the remaining n-1 elements), or we don't select element x (in which case we still
need to choose k of the remaining n-1 elements).

The code for calculating binomial coefficients is shown below


long binomial(int n, int k)
{
if (k == 0) return 1;
if (n == 0) return 0;
return binomial(n-1, k) + binomial(n-1, k-1);

b. Find the Binomial Co-efficient using Dynamic Programming.

#include<iostream.h>
#include<conio.h>

int c[20][20],n,k;

class bino
{
public: int min(int a,int b);
void binomial(int n,int k);
void bino::read();
};

int bino::min(int a,int b)


{
if(a<b)
return a;
else
return b;
}
void bino::binomial(int n,int k)
{
int i,j;
cout<<"\n The table of co-efficients are...\n";
for(i=0;i<=n;i++)
{
for(j=0;j<=min(i,k);j++)
{
if(j==0 || j==i)
c[i][j]=1;
else
c[i][j]=c[i-1][j]+c[i-1][j-1];
// cout<<"\t"<<c[i][j];
}
cout<<"\n";
}
cout<<"\n The binomial co-efficient = "<<c[n][k];
}

void bino::read()
{
cout<<"\n Enter n:";
cin>>n;
cout<<"\n Enter k:";
cin>>k;
if(k<0 || n<0 || n<k)
cout<<"\n Invalid entry";
else
binomial(n,k);
getch();
}

void main()
{
clrscr();
bino b;
b.read();
}
******************************************************************************
*
/* OUTPUT

RUN 1:
Enter n:5

Enter k:3

The table of co-efficients are...


1
1 1
1 2 1
1 3 3 1
1 4 6 4
1 5 10 10
The binomial co-efficient = 10

RUN 2:
Enter n:3

Enter k:7

Invalid entry
*/

******************************************************************************
*

Prim’s Algorithm
Prim's algorithm is an algorithm in graph theory that finds a minimum spanning tree for
a connected weighted graph. This means it finds a subset of the edges that forms a tree that
includes every vertex, where the total weight of all the edges in the tree is minimized. The
algorithm was developed in 1930 by Czech mathematician Vojtěch Jarník and later
independently by computer scientist Robert C. Prim in 1957 and rediscovered by Edsger Dijkstra
in 1959. Therefore it is sometimes called the DJP algorithm, the Jarník algorithm, or the
Prim-Jarník algorithm.

The algorithm continuously increases the size of a tree starting with a single vertex until it spans
all the vertices.

• Input: A connected weighted graph with vertices V and edges E.

• Initialize: Vnew = {x}, where x is an arbitrary node (starting point) from V, Enew = {}

• Repeat until Vnew = V:

• Choose edge (u,v) with minimal weight such that u is in V new and v is not (if there
are multiple edges with the same weight, choose arbitrarily but consistently)

• Add v to Vnew, add (u, v) to Enew

• Output: Vnew and Enew describe a minimal spanning tree


• Find Minimum Cost Spanning Tree of a given undirected graph using
Prim’s algorithm.
#include<iostream.h>
#include<conio.h>

int i,j,count=1,source,curr,cost[10][10],r[10],s[10],sum=0;
int visit[100],d[100],n;

class prims
{
public: void read();
int min(int a,int b);
int getcur();
void MCST();
};
int prims::min(int a,int b)
{
if(a<=b)
return a;
else
return b;

int prims::getcur()
{
int i=1,curr;
while(visit[i]==1)
i++;
curr=i;
for(i=curr+1;i<=n;i++)
if(d[i]<d[curr] && visit[i]==0)
curr=i;
return curr;
}

void prims::read()
{
cout<<"\n Enter the number of nodes:";
cin>>n;
cout<<"\n Enter the cost matrix:\n";
cout<<"\nEnter 0->diagonal elements and 99->No direct edge cost\n";
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
cin>>cost[i][j];
visit[i]=0;
}
}
}

void prims::MCST()
{
for(i=1;i<=n;i++)
{
d[i]=cost[1][i];
r[i]=1;
}

visit[1]=1;
while(count<n)
{
curr=getcur();
visit[curr]=1;
count++;
for(i=1;i<=n;i++)
if(visit[i]==0)
{
s[i]=d[i];
d[i]=min(d[i],cost[curr][i]);
if(d[i]!=s[i])
r[i]=curr;
}
}
cout<<"\n EDGES WEIGHTS\n";
for(i=1;i<=n;i++)
cout<<"\n("<<r[i]<<","<<i<<")"<<"--->"<<d[i];
cout<<"\nThe minimum cost spanning tree is\t";
for(i=1;i<=n;i++)
sum=sum+d[i];
cout<<sum;
}

void main()
{
prims p;
clrscr();
p.read();
p.MCST();
getch();
}

/*--------------------------------------------------------------------
output:

Enter the number of nodes: 5


Enter the cost matrix:
Enter 0->diagonal elements and 99->No direct edge cost
0 1 3 99 5
1 0 4 6 99
3 4 0 2 6
99 6 2 0 99
5 99 6 99 0

EDGES WEIGHTS
(1,1)---------> 0
(1,2)---------> 1
(1,3)---------> 3
(3,4)---------> 2
(1,5)---------> 5

The minimum cost spanning tree is 11


----------------------------------------------------------------------*/

Warshall’s Algorithm

Warshall's algorithm enables to compute the transitive closure of the adjacency matrix
of any digraph. The transitive closure of a digraph G=(V,E) with set of vertices and
edges is defined as Boolean matrix (only 0’s and 1’s ) of size n X n. Such that
a[i,,j]= 1 if there is a non trival directed path
else a[i,j]=0
Here input is an digraph it should be supplied as an adjucancy matrix.

• a. Compute the transitive closure of a given directed graph using


Warshall's algorithm.

#include<iostream.h>
#include<conio.h>

int i,j,k,n,a[10][10],p[10][10],flag=1;

class tclose
{
public: void warshall(int n, int a[10][10], int p[10][10]);
void read(int n, int a[10][10]);
void write(int n, int a[10][10]);
};

void tclose::warshall(int n, int a[10][10], int p[10][10])


{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
p[i][j]=a[i][j];

for(k=0;k<n;k++)
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if((p[i][k]==1 && p[k][j]==1))
p[i][j]=1;
}

void tclose::read(int n, int a[10][10])


{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
cin>>a[i][j];
}

void tclose::write(int n, int a[10][10])


{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
cout<<a[i][j]<<"\t";
if(a[i][j]!=1)
flag=0;
}
cout<<"\n";
}
/*if(flag)
cout<<"\n Graph is connected";
else
cout<<"\n Graph is disconnected";*/
}

void main()
{
tclose t;
clrscr();
cout<<"\n enter the no. of nodes:";
cin>>n;
cout<<"enter the adjacent matrix:";
t.read(n,a);
t.warshall(n,a,p);
cout<<"The Transitive closure(path matrix)\n";
t.write(n,p);
getch();
}

******************************************************************************
*
/* -----OUTPUT-----

Enter the no of nodes : 4

Enter the Adjucency Matrix


0 1 0 0
0 0 0 1
0 0 0 0
1 0 1 0

The closute matrix is :

1 1 1 1
1 1 1 1
0 0 0 0
1 1 1 1
*/

******************************************************************************
*

13 b. Implement Floyds algorithm for the all pairs Shortest path problem.

#include<iostream.h>
#include<conio.h>
int i,j,k,n;
int cost[10][10],d[10][10];

class floy
{
public: int min(int a , int b);
void floyd(int n, int cost[10][10], int d[10][10]);
void read(int n, int a[10][10]);
void write(int n, int a[10][10]);
};

int floy::min(int a , int b)


{
return a<b?a:b;
}

void floy::floyd(int n, int cost[10][10], int d[10][10])


{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
d[i][j]=cost[i][j];

for(k=0;k<n;k++)
{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}

void floy::read(int n, int a[10][10])


{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
cin>>a[i][j];
}

void floy::write(int n, int a[10][10])


{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
cout<<"\t"<<a[i][j];
}
cout<<"\n";
}
}

void main()
{
clrscr();
floy f;
cout<<"Enter the number of nodes:\n";
cin>>n;
cout<<"\nEnter the cost adjacency matrix:\n";
cout<<"Enter0->Self loop 99->No direct Edge cost:\n\n";
f.read(n,cost);
f.floyd(n,cost,d);
cout<<"\nSolution to all pairs shortest path problem is:\n";
f.write(n,d);
getch();
}

******************************************************************************
*
/* -----OUTPUT-----
Enter the no of nodes : 4

Enter the Cost Adjucency Matrix


Enter0->Self loop 99->No direct Edge cost:
0 99 3 99
2 0 99 99
99 7 0 1
6 99 99 0

nSolution to all pairs shortest path problem is

0 10 3 4
2 0 5 6
7 7 0 1
6 16 9 0
*/
******************************************************************************
*

N Queen Problem
The n-queens problem consists in placing n non-attacking queens on an n-by-n chess
board. A queen can attack another queen vertically, horizontally, or diagonally. E.g.
placing a queen on a central square of the board blocks the row and column where it is
placed, as well as the two diagonals (rising and falling) at whose intersection the queen
was placed. The algorithm to solve this problem uses backtracking, but we will unroll
the recursion. The basic idea is to place queens column by column, starting at the left.
New queens must not be attacked by the ones to the left that have already been placed
on the board. We place another queen in the next column if a consistent position is
found. All rows in the current column are checked. We have found a solution if we
placed a queen in the rightmost column.

• Implement N Queen's problem using Back Tracking.

#include<iostream.h>
#include<conio.h>
#include<stdlib.h>

int a[100],count=0, i,n;


class queen
{
public: int place(int pos);
void printsol(int n);
void nqueens(int n);
void read();
};
int queen::place(int pos)
{
int i;
for(i=1;i<pos;i++)
{
if((a[i]==a[pos])||((abs(a[i]-a[pos])==abs(i-pos))))
return 0;
}
return 1;
}
void queen::printsol(int n)
{
int i,j;
count++;
cout<<"\n\nSolution: "<<count;
for(i=1;i<=n;i++)
{
cout<<"\n\n ";
for(j=1;j<=n;j++)
{
if(a[j]==i)
cout<<"Q ";
else
cout<<"* ";
}
getch();
}
}
void queen::nqueens(int n)
{
int k=1;
a[k]=0;
while(k!=0)
{
a[k]=a[k]+1;
while(a[k]<=n && !place(k))
a[k]++;
if(a[k]<= n)
if(k==n)
printsol(n);
else
{
k++;
a[k]=0;
}
else
k--;
}
}
void queen::read()
{
cout<<"\nEnter the no of queens : ";
cin>>n;
if(n==2||n==3)
{
cout<<"No solution:\n";
getch();
exit(0);
}
nqueens(n);
cout<<"\nTotal no of solutions =" <<count;
getch();
}
void main()
{
queen q;
clrscr();
q.read();
}
/* -----OUTPUT-----
Enter the no of queens : 4

Solution: 1
* * Q *
Q * * *
* * * Q
* Q * *
Solution: 2
* Q * *
* * * Q
Q * * *
* * Q *
Total no of solutions = 2*/

Algorithms Lab VIVA Questions


• Difference between recursive and non-recursive
Algorithm
• Define Algorithm and Mention various methods of
Specifying an algorithm
• What is analysis and Design of algorithm
• Difference between Binary search and linear
search.
• Define heap and Explain construction of Heap
with one numeric example
5,35,25,45,20,55,25,45,50,10,30 and 15
• Difference between Merge sort and Bubble sort

• Define dynamic programming with example

• Explain the Concept of greedy method by


considering change making problem

• Difference between DFS and BFS

• Define backtracking

• Explain Horspool algorithm with text string

• Difference between Kruskal’s and Prim’s


algorithm

• For the given graph implement floyd’s Alogrithm

• Explain the concept of Dijksthra’s algorithm


with example

• Difference between Floyd’s and Warshall’s


algorithm

• Explain concept of knapsack Problem using


dynamic programming

You might also like