Aoa Practicals
Aoa Practicals
EXP 5
Implement Minimum cost spanning trees using Kruskal algorithm.
Theory: The Kruskal algorithm is a greedy algorithm used to find the minimum cost spanning
tree of a graph. It operates by initially sorting the edges of the graph by their weights, then
iteratively selecting the edges with the lowest weight while ensuring that adding them to the
spanning tree does not create a cycle. This process continues until all vertices are connected,
resulting in a minimum cost spanning tree. The Kruskal algorithm is efficient and
straightforward to implement, typically utilizing disjoint-set data structures to efficiently
detect and merge disjoint sets of vertices. It guarantees the construction of a minimum cost
spanning tree and is particularly useful for sparse graphs or situations where edge weights
are distinct. However, it may not be as efficient for dense graphs compared to other
algorithms like Prim's algorithm.
Codes:
#include <stdio.h>
#include <stdlib.h>
int n, i, j, k, a, b, u, v, ne, mincost;
int cost[9][9], parent[9];
int find(int);
int unionSet(int, int);
void main() {
printf("\n\n\tImplementation of Kruskal's algorithm\n");
printf("Enter the number of vertices: ");
scanf("%d", &n);
printf("\nEnter the cost adjacency matrix:\n");
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
scanf("%d", &cost[i][j]);
if (cost[i][j] == 0)
cost[i][j] = 500; // Assuming 500 is a large enough value to represent infinity
}
}
ne = 1;
mincost = 0;
printf("\nThe edges of Minimum Cost Spanning Tree are:\n");
while (ne < n) {
for (i = 1, mincost = 500; i <= n; i++) {
for (j = 1; j <= n; j++) {
if (cost[i][j] < mincost) {
mincost = cost[i][j];
a = u = i;
b = v = j;
}
}
}
u = find(u);
v = find(v);
if (unionSet(u, v)) {
printf("%d edge (%d,%d) = %d\n", ne++, a, b, mincost);
mincost = mincost + cost[a][b];
}
cost[a][b] = cost[b][a] = 500; // Marking this edge as visited
}
printf("\nMinimum cost = %d\n", mincost);
}
int find(int i) {
while (parent[i])
i = parent[i];
return i;
}
int unionSet(int i, int j) {
if (i != j) {
parent[j] = i;
return 1;
}
return 0;
}
EXP 6
Implement single source shortest path- Bellman Ford algorithm.
Theory: The Bellman-Ford algorithm is a dynamic programming-based approach used to find
the shortest paths from a single source vertex to all other vertices in a weighted graph,
including graphs with negative edge weights, albeit not negative cycles. The algorithm
iterates through all edges of the graph repeatedly, relaxing each edge to minimize the
distance to its destination vertex. This process is repeated for a number of iterations equal to
the number of vertices minus one, ensuring that the shortest paths are found even in the
presence of negative edge weights. However, if a negative cycle exists, the algorithm can
detect it in the last iteration, as the shortest paths would continue to decrease in length
indefinitely. The Bellman-Ford algorithm's time complexity is O(V*E), where V is the number
of vertices and E is the number of edges, making it less efficient than Dijkstra's algorithm for
most cases but more versatile due to its ability to handle negative edge weights.
Codes:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
struct Edge
{
// This structure is equal to an edge. Edge contains two end points. These edges are
directed edges so they
//contain source and destination and some weight. These 3 are elements in this structure
int source, destination, weight;
};
// a structure to represent a connected, directed and weight graph
struct Graph
{
int V, E;
//V is number of vertices and E is number of edges
struct Edge* edge;
//This structure contain another structure which we already created edge.
};
struct Graph* createGraph(int V, int E)
{
struct Graph* graph=(struct Graph*)malloc(sizeof(struct Graph));
//Allocating space to structure graph
graph->V=V; //assigning values to structure elements that taken from user.
graph->E=E;
graph->edge=(struct Edge*)malloc(graph->E*sizeof(struct Edge));
//Creating "Edge" type structures inside "Graph" structure, the number of edge type
structures are equal to number of edges
return graph;
}
void FinalSolution(int dist[],int n)
{
//This function prints the final FinalSolution
printf("\nVertex\tDistance from Source Vertex\n");
int i;
for(i=0; i<n;++i)
{
printf("%d\t\t%d\n",i,dist[i]);
}
}
void BellmanFord(struct Graph* graph,int source)
{
int V=graph->V;
int E=graph->E;
int StoreDistance[V];
int i,j;
//This is initial step that we know, we initialise all distance to infinity except source.
//We assign source distance as 0(zero)
for (i=0;i<V;i++)
StoreDistance[source]=0;
//The shortest path of graph that contain V vertices, never contain "V-1" edges. So we do
here "V-1" relaxations
for(i=1;i<=V-1;i++)
{
for(j=0;j<E;j++)
{
int u = graph->edge[j].source;
int v = graph->edge[j].destination;
int weight = graph->edge[j].weight;
if (StoreDistance[u]+weight<StoreDistance[v])
StoreDistance[v]=StoreDistance[u]+weight;
}
}
//If we get a shorter path, then there is a negative edge cycle.
for(j=0;j<E;j++)
{
int u = graph->edge[j].source;
int v = graph->edge[j].destination;
int weight = graph->edge[j].weight;
if (StoreDistance[u]+weight<StoreDistance[v])
printf("This graph contains negative edge cycle\n");
}
FinalSolution(StoreDistance,V);
return;
}
int main()
{
int V,E,S; //V=no.of Vertices, E=no.of Edges, S is source Vertex
printf("Enter number of vertices in graph\n");
scanf("%d",&V);
printf("Enter number of edges in graph\n");
scanf("%d",&E);
printf("Enter your source vertex number\n");
scanf("%d",&S);
struct Graph*graph=createGraph(V,E); //Calling the function to allocate space to these
many vertices and edges
int i;
for(i=0;i<E;i++)
{
printf("\nEnter edge %d properties Source, destination, weight respectively\n",i+1);
scanf("%d",&graph->edge[i].source);
scanf("%d",&graph->edge[i].destination);
scanf("%d",&graph->edge[i].weight);
}
BellmanFord(graph, S);
//passing created graph and source vertex to BellmanFord Algorithm function
return 0;}
EXP 7
Implement all pairs shortest path- Floyd Warshall Algorithm.
Theory: The Floyd-Warshall algorithm is a dynamic programming-based approach used to
find the shortest paths between all pairs of vertices in a weighted graph. It operates by
iteratively considering all possible intermediate vertices in each path and updating the
shortest distance between every pair of vertices accordingly. The algorithm initializes a two-
dimensional array to store the shortest distances between all pairs of vertices, initially
setting them to the weights of the edges connecting the vertices directly. It then iterates
through all possible intermediate vertices, updating the shortest distances if a shorter path
is found through the current intermediate vertex. This process continues until all vertices
have been considered, resulting in the shortest paths between all pairs of vertices being
computed. The time complexity of the Floyd-Warshall algorithm is O(V^3), where V is the
number of vertices, making it suitable for dense graphs with relatively small numbers of
vertices. However, it may be less efficient than algorithms such as Dijkstra's or Bellman-Ford
for sparse graphs due to its higher time complexity. Nonetheless, it is widely used for its
simplicity and ability to handle both positive and negative edge weights, as well as detect
negative cycles in the graph.
Codes:
#include<stdio.h>
int i, j, k, n, dist[10][10];
void floyWarshell()
{
for(k=0;k<n;k++)
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if (dist[i][k]+dist[k][j]<dist[i][j])
dist[i][j]=dist[i][k]+dist[k][j];
}
int main()
{
int i, j;
printf("enter no of vertices:");
scanf("%d",&n);
printf("\n");
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
printf("dist[%d][%d]:",i,j);
scanf("%d",&dist[i][j]);
}
floyWarshell();
printf("\n\n shortest distances between every pair of vertices \n");
for(int i=0;i<n;i++)
{
for (int j=0;j<n;j++)
printf("%d\t",dist[i][j]);
printf("\n");
}
return 0;
}
EXP 8
Implement 8-queens problem using backtracking approach.
Theory: Implementing the 8-queens problem using a backtracking approach involves
systematically placing 8 queens on an 8x8 chessboard in such a way that no two queens
threaten each other. The backtracking algorithm starts by placing a queen in the first column
and recursively explores all possible placements in subsequent columns, backtracking
whenever a conflict is detected. It checks each potential position to ensure that it is not in
the same row, column, or diagonal as any previously placed queens. If a safe position is
found for all queens, the algorithm returns a solution; otherwise, it backtracks to explore
alternative placements until all possibilities are exhausted. Backtracking is an efficient
method for solving combinatorial problems like the 8-queens problem, as it prunes the
search space by discarding branches that lead to conflicts, leading to a significant reduction
in the number of combinations to explore.
Codes:
#include<stdio.h>
#include<math.h>
int board[20], count;
void queen(int row, int n);
void print(int n);
int place(int row, int column);
int main() {
int n;
printf("- N Queens Problem Using Backtracking -");
printf("\n\nEnter number of Queens:");
scanf("%d", &n);
queen(1, n);
return 0;
}
// function for printing the solution
void print(int n) {
int i, j;
printf("\n\nSolution %d:\n\n", ++count);
for (i = 1; i <= n; ++i)
printf("\t%d", i);
for (i = 1; i <= n; ++i) {
printf("\n\n%d", i);
for (j = 1; j <= n; ++j) { // for nxn board
if (board[i] == j)
printf("\tQ"); // queen at i,j position
else
printf("\t-"); // empty slot
}
}
}
/* function to check conflicts
If no conflict for desired position returns 1 otherwise returns 0 */
int place(int row, int column) {
int i;
for (i = 1; i <= row - 1; ++i) {
// checking column and diagonal conflicts
if (board[i] == column || abs(board[i] - column) == abs(i - row))
return 0;
}
return 1; // no conflicts
}
// function to solve N Queens problem using backtracking
void queen(int row, int n) {
int column;
for (column = 1; column <= n; ++column) {
if (place(row, column)) {
board[row] = column;
if (row == n) // if all queens are placed
print(n); // then print the solution
else
queen(row + 1, n); // try next row
}
}
}
EXP 9
Implement 15 puzzle problem using branch and bound design method.
Theory: Implementing the 15 puzzle problem using the branch and bound design method
involves systematically exploring the state space of the puzzle while keeping track of the
minimum cost solution found so far. The branch and bound algorithm starts by generating all
possible moves from the initial state and adding them to a priority queue based on their
estimated cost. It then iteratively selects and expands the node with the lowest cost,
updating the priority queue with its successor states. During this process, the algorithm
prunes branches of the search tree that cannot lead to a solution better than the current
best solution, reducing the overall search space. The algorithm terminates when a goal state
is reached or when all nodes in the priority queue have higher costs than the current best
solution. By systematically exploring the state space while keeping track of the minimum
cost solution, the branch and bound method efficiently finds an optimal solution to the 15
puzzle problem.
Codes:
#include<stdio.h>
int m = 0, n = 4;
int cal(int temp[10][10], int t[10][10]) {
int i, j, m = 0;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (temp[i][j] != t[i][j])
m++;
return m;
}
int check(int a[10][10], int t[10][10]) {
int i, j, f = 1;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (a[i][j] != t[i][j])
f = 0;
return f;
}
void main() {
int p, i, j, a[10][10], t[10][10], temp[10][10], r[10][10];
int x = 0, y = 0, d = 1000, dmin = 0, l = 0;
printf("\nEnter the matrix to be solved, space with zero:\n");
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &a[i][j]);
printf("\nEnter the target matrix, space with zero:\n");
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &t[i][j]);
printf("\nEntered Matrix is :\n");
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
printf("%d\t", a[i][j]);
printf("\n");
}
printf("\nTarget Matrix is :\n");
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
printf("%d\t", t[i][j]);
printf("\n");
}
while (!(check(a, t))) {
l++;
d = 1000;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (a[i][j] == 0) {
x = i;
y = j;
}
// To move upwards
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
temp[i][j] = a[i][j];
if (x != 0) {
p = temp[x][y];
temp[x][y] = temp[x - 1][y];
temp[x - 1][y] = p;
}
m = cal(temp, t);
dmin = l + m;
if (dmin < d) {
d = dmin;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
r[i][j] = temp[i][j];
}
// To move downwards
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
temp[i][j] = a[i][j];
if (x != n - 1) {
p = temp[x][y];
temp[x][y] = temp[x + 1][y];
temp[x + 1][y] = p;
}
m = cal(temp, t);
dmin = l + m;
if (dmin < d) {
d = dmin;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
r[i][j] = temp[i][j];
}
// To move right side
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
temp[i][j] = a[i][j];
if (y != n - 1) {
p = temp[x][y];
temp[x][y] = temp[x][y + 1];
temp[x][y + 1] = p;
}
m = cal(temp, t);
dmin = l + m;
if (dmin < d) {
d = dmin;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
r[i][j] = temp[i][j];
}
// To move left
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
temp[i][j] = a[i][j];
if (y != 0) {
p = temp[x][y];
temp[x][y] = temp[x][y - 1];
temp[x][y - 1] = p;
}
m = cal(temp, t);
dmin = l + m;
if (dmin < d) {
d = dmin;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
r[i][j] = temp[i][j];
}
printf("\nCalculated Intermediate Matrix Value:\n");
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
printf("%d\t", r[i][j]);
printf("\n");
}
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) {
a[i][j] = r[i][j];
temp[i][j] = 0;
}
printf("Minimum cost : %d\n", d);
}
}
EXP 10
Implement Rabin Karp/Naive string-matching algorithm.
Theory: The Rabin-Karp algorithm and the Naive string-matching algorithm are two
commonly used methods for finding occurrences of a pattern within a text. The Naive
approach involves systematically comparing the pattern with substrings of the text, sliding
the pattern one character at a time and checking for matches at each position. While simple,
it can be inefficient, especially for large texts, as it may require examining all substrings. On
the other hand, the Rabin-Karp algorithm improves efficiency by using a hashing technique
to quickly compare the hash values of the pattern and substrings of the text, only
performing a full comparison if the hash values match. This reduces the number of
comparisons needed, making it more efficient than the Naive approach, especially for longer
patterns and texts. However, the Rabin-Karp algorithm requires careful selection of hashing
functions to minimize the risk of collisions and false positives, and its worst-case time
complexity is still O(nm), where n is the length of the text and m is the length of the pattern.
Codes:
#include <stdio.h>
#include <string.h>
int flag;
// Naive String Matching Algorithm
void naive_search(char* pattern, char* text) {
int m = strlen(pattern);
int n = strlen(text);
int i, j;
flag = 1;
for (i = 0; i <= n - m; i++) {
for (j = 0; j < m; j++) {
if (text[i + j] != pattern[j])
break;
}
if (j == m) {
flag = 0;
printf("Pattern found at index %d\n", i);
}
}
}
// Rabin-Karp Algorithm
void rabin_karp_search(char* pattern, char* text, int q) {
int m = strlen(pattern);
int n = strlen(text);
int i, j;
int p = 0; // hash value for pattern
int t = 0; // hash value for current substring of text
int h = 1; // d^(m-1) % q
flag = 1;
// Calculate h
for (i = 0; i < m - 1; i++)
h = (h * 256) % q;
// Calculate initial hash values
for (i = 0; i < m; i++) {
p = (256 * p + pattern[i]) % q;
t = (256 * t + text[i]) % q;
}
// Slide the pattern over text one by one
for (i = 0; i <= n - m; i++) {
// Check if the hash values of current substring of text and pattern are equal
if (p == t) {
// Check if all characters match
for (j = 0; j < m; j++) {
if (text[i + j] != pattern[j])
break;
}
if (j == m) {
flag = 0;
printf("Pattern found at index %d\n", i);
}
}
// Calculate hash value for next substring of text
if (i < n - m) {
t = (256 * (t - text[i] * h) + text[i + m]) % q;
if (t < 0)
t += q;
}
}
}
int main() {
int choice, q;
char pattern[100], text[100];
while (1) {
printf("\nString Matching Algorithms:\n");
printf("1. Naive String Matching\n");
printf("2. Rabin-Karp Algorithm\n");
printf("3. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("Enter the text: ");
scanf("%s", text);
printf("Enter the pattern: ");
scanf("%s", pattern);
naive_search(pattern, text);
if (flag == 1)
printf("No match found\n");
break;
case 2:
printf("Enter the text: ");
scanf("%s", text);
printf("Enter the pattern: ");
scanf("%s", pattern);
printf("Enter a prime number: ");
scanf("%d", &q);
rabin_karp_search(pattern, text, q);
if (flag == 1)
printf("No match found\n");
break;
case 3:
printf("Exiting program.\n");
return 0;
default:
printf("Invalid choice. Try again.\n");
}
}
return 0;
}