Download as pdf or txt
Download as pdf or txt
You are on page 1of 20

Important keywords with details

1. C Program
2. Graphs, directed graph and undirected graphs
3. Minimum Cost Spanning Tree
4. Kruskal's algorithm
5. Prim's algorithm

6. All-Pairs Shortest Paths problem


7. Floyd's algorithm
8. find the transitive closure
9. Warshal's algorithm
10. shortest paths from a given vertex in a weighted connected graph
11. Dijkstra's algorithm
12. Topological ordering of vertices
13. Digraph

14. 0/1 Knapsack problem


15. Dynamic Programming method
16. solve discrete Knapsack and continuous Knapsack Problems
17. greedy approximation method

18. 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.

19. Selection Sort method


20. Quick Sort method
21. Merge Sort method

22. N Queen's problem


23. Backtracking
Graph: A graph G consists of a set of vertices V and a set of edges E. The edges can
be directed or undirected.
Vertex (or Node): A fundamental unit of a graph.
Edge: A connection between two vertices.
Weight: A value associated with an edge, representing the cost, distance, or any other
metric.

Directed Graph (Digraph): In a digraph, each edge has a direction, meaning it goes
from one vertex to another, indicated by an arrow.

Undirected Graph: In an undirected graph, edges have no direction, and they simply
connect two vertices.

Common Operations
Add Vertex: Add a new vertex to the graph.
Add Edge: Add a new edge between two vertices.
Remove Vertex: Remove a vertex and its associated edges.
Remove Edge: Remove an edge between two vertices.
Traverse: Visit all vertices and edges of the graph.

A Minimum Cost Spanning Tree (MCST) of a connected, undirected graph is a


subgraph that includes all the vertices of the original graph and is a tree (acyclic).
The total weight of an MCST is minimized compared to other possible spanning trees.

Properties
An MCST has n−1 edges for n vertices.
An MCST doesn't contain any cycles.
An MCST connects all vertices in a graph with the minimum total edge weight.
1. Design and implement C/C++ Program to find Minimum Cost Spanning Tree
of a given connected undirected graph using Kruskal's algorithm.

Kruskal's algorithm is a greedy algorithm used to find the Minimum Spanning Tree
(MST) of a connected, undirected graph. The algorithm grows a forest of trees by
adding the smallest edge that doesn't form a cycle until all vertices are connected.

Steps of Kruskal's Algorithm


Initialization:
Create a forest F (a collection of trees), where each vertex is a separate tree.
Create a priority queue (or sorted list) of all edges in the graph, sorted by their
weights.

Iterate over Edges:


While F doesn't form a single tree (i.e., number of trees > 1) and the priority queue is
not empty:
Remove the smallest edge e from the priority queue.

Check Cycle:
If adding e to F doesn't form a cycle (i.e., the two vertices of e are in different trees),
add e to the MST T.

Update Forest:
Merge the two trees connected by e into one tree.

Termination:
When F forms a single tree, T is the MST.

Example
Let's consider a graph with vertices A,B,C,D,E and edges with weights as follows:
Termination:
T is the Minimum Spanning Tree with total weight = 1+1+2+3+4=11

Implementation in C

#include <stdio.h>
#include <stdlib.h>

// Structure to represent an edge in the graph


typedef struct Edge {
int src, dest, weight;
} Edge;

// Structure to represent a subset for union-find


typedef struct Subset {
int parent;
int rank;
} Subset;

// Function prototypes
int find(Subset subsets[], int i);
void Union(Subset subsets[], int x, int y);
void kruskalMST(Edge edges[], int V, int E);

// Compare function for sorting edges by weight


int compare(const void* a, const void* b) {
Edge* edgeA = (Edge*)a;
Edge* edgeB = (Edge*)b;
return edgeA->weight - edgeB->weight;
}
// Main function
int main() {
int V = 5; // Number of vertices
int E = 6; // Number of edges
Edge edges[] = {
{0, 1, 1}, {0, 3, 4}, {1, 3, 2},
{1, 4, 3}, {2, 3, 5}, {2, 4, 1}
};

kruskalMST(edges, V, E);
return 0;
}

// Function to find set of an element i (uses path compression technique)


int find(Subset subsets[], int i) {
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent);
return subsets[i].parent;
}

// Function to perform union of two sets x and y (uses union by rank)


void Union(Subset subsets[], int x, int y) {
int xroot = find(subsets, x);
int yroot = find(subsets, y);
if (subsets[xroot].rank < subsets[yroot].rank)
subsets[xroot].parent = yroot;
else if (subsets[xroot].rank > subsets[yroot].rank)
subsets[yroot].parent = xroot;
else {
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
}

// Function to find Minimum Spanning Tree using Kruskal's algorithm


void kruskalMST(Edge edges[], int V, int E) {
Edge result[V]; // Array to store MST
int e = 0; // Index variable for result array
int i = 0; // Index variable for sorted edges array

// Step 1: Sort all edges by weight


qsort(edges, E, sizeof(Edge), compare);

// Allocate memory for creating V subsets


Subset* subsets = (Subset*)malloc(V * sizeof(Subset));

// Create V subsets with single elements


for (int v = 0; v < V; v++) {
subsets[v].parent = v;
subsets[v].rank = 0;
}

// Number of edges to be taken is V-1


while (e < V - 1 && i < E) {
Edge next_edge = edges[i++];
int x = find(subsets, next_edge.src);
int y = find(subsets, next_edge.dest);

// If including this edge doesn't cause cycle, include it in result


if (x != y) {
result[e++] = next_edge;
Union(subsets, x, y);
}
}

// Print the edges of MST


printf("Minimum Spanning Tree Edges:\n");
int total_weight = 0;
for (int j = 0; j < e; j++) {
printf("%d -- %d : %d\n", result[j].src, result[j].dest, result[j].weight);
total_weight += result[j].weight;
}
printf("Total Weight of MST: %d\n", total_weight);

// Free memory
free(subsets);
}
2. Design and implement C/C++ Program to find Minimum Cost Spanning Tree
of a given connected undirected graph using Prim's algorithm.

Prim's algorithm is a greedy algorithm used to find the Minimum Spanning Tree
(MST) of a connected, undirected graph. Unlike Kruskal's algorithm, which focuses
on edges, Prim's algorithm focuses on vertices. The algorithm grows a single tree (or
forest) by adding the closest vertex that is not already in the tree until all vertices are
included.

Steps of Prim's Algorithm

Initialization:
Select an arbitrary vertex v to start the MST.
Initialize an empty set S to store vertices included in the MST and a priority queue (or
min heap) to store edges.

Grow the MST:


While S doesn't include all vertices:
Find the edge with the minimum weight that connects a vertex in S to a vertex not in
S.
Add the new vertex and edge to S.

Termination:
When S includes all vertices, the MST is complete.

Example
Let's consider a graph with vertices A,B,C,D,E and edges with weights as follows:
0 1 2 3 4
0 0 1 0 4 0
1 1 0 0 2 3
2 0 0 0 5 1
3 4 2 5 0 0
4 0 3 1 0 0

Vertex 0:
Connected to vertex 1 with weight 1
Connected to vertex 3 with weight 4
Vertex 1:
Connected to vertex 0 with weight 1
Connected to vertex 3 with weight 2
Connected to vertex 4 with weight 3
Vertex 2:
Connected to vertex 3 with weight 5
Connected to vertex 4 with weight 1
Vertex 3:
Connected to vertex 0 with weight 4
Connected to vertex 1 with weight 2
Connected to vertex 2 with weight 5
Vertex 4:
Connected to vertex 1 with weight 3
Connected to vertex 2 with weight 1

Implementation in C

#include <stdio.h>
#include <limits.h>

#define V 5
#define INF INT_MAX

int minKey(int key[], int mstSet[]) {


int min = INF, min_index;
for (int v = 0; v < V; v++) {
if (mstSet[v] == 0 && key[v] < min) {
min = key[v];
min_index = v;
}
}
return min_index;
}

void primMST(int graph[V][V]) {


int parent[V]; // To store constructed MST
int key[V]; // Key values used to pick minimum weight edge
int mstSet[V]; // To represent set of vertices not yet included in MST
// Initialize all keys as INFINITE and mstSet[] as 0
for (int i = 0; i < V; i++) {
key[i] = INF;
mstSet[i] = 0;
}

// Always include the first vertex in MST


key[0] = 0; // Make key 0 so that this vertex is picked as the first vertex
parent[0] = -1; // First node is always root of MST

// The MST will have V vertices


for (int count = 0; count < V - 1; count++) {
int u = minKey(key, mstSet);
mstSet[u] = 1;

// Update key value and parent index of adjacent vertices


for (int v = 0; v < V; v++) {
if (graph[u][v] && mstSet[v] == 0 && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}

// Print the constructed MST and calculate total weight


int totalWeight = 0;
printf("Edge Weight\n");
for (int i = 1; i < V; i++) {
printf("%d - %d %d \n", parent[i], i, graph[i][parent[i]]);
totalWeight += graph[i][parent[i]];
}
printf("Total Weight of MST: %d\n", totalWeight);
}

int main() {
int graph[V][V] = {
{0, 1, 0, 4, 0},
{1, 0, 0, 2, 3},
{0, 0, 0, 5, 1},
{4, 2, 5, 0, 0},
{0, 3, 1, 0, 0}
};

primMST(graph);
return 0;
}
3. a. Design and implement C/C++ Program to solve All-Pairs Shortest Paths
problem using Floyd's algorithm.
b. Design and implement C/C++ Program to find the transitive closure using
Warshal's algorithm.

The All-Pairs Shortest Paths (APSP) problem is a classic problem in computer science
and graph theory. The goal of this problem is to find the shortest paths between every
pair of vertices in a weighted graph. The weights on the edges can represent distances,
costs, or any other metric, and can be both positive and negative.

There are several algorithms to solve the APSP problem, each with its own
advantages and disadvantages:

Floyd-Warshall Algorithm:
This is one of the most famous algorithms for solving the APSP problem.
It works for both directed and undirected graphs with positive or negative edge
weights.
The time complexity is O(V^3), where V is the number of vertices.

Johnson's Algorithm:
This algorithm is more efficient than Floyd-Warshall for sparse graphs (graphs with
relatively few edges).
It first transforms the edge weights using the Bellman-Ford algorithm to make them
non-negative, and then uses Dijkstra's algorithm for each vertex as the source.
The time complexity depends on the implementation, but it's generally better than
O(V^3) for sparse graphs.

Bellman-Ford Algorithm (for single-source shortest paths):


Although primarily designed for single-source shortest paths, it can be used as a
component in other algorithms like Johnson's algorithm.
It can handle negative edge weights but not negative cycles.

Dijkstra's Algorithm (for single-source shortest paths):


Similar to Bellman-Ford, Dijkstra's algorithm can be used as a component in
algorithms like Johnson's.
It works efficiently for graphs with non-negative edge weights.

The All-Pairs Shortest Paths (APSP) problem involves finding the shortest paths
between all pairs of vertices in a weighted graph. This problem is a generalization of
the Single-Source Shortest Path problem (like Dijkstra's or Bellman-Ford algorithms),
where the shortest path from a single source vertex to all other vertices is found.

Floyd's algorithm, also known as the Floyd-Warshall algorithm, is a dynamic


programming algorithm used to solve the APSP problem. The algorithm works by
considering all possible intermediate vertices in the paths and iteratively updating the
shortest path distances between pairs of vertices.

Floyd's Algorithm:
Initialization: Create a distance matrix dist[][] where dist[i][j] stores the shortest
distance between vertex i and vertex j. Initialize dist[i][j] to the weight of the edge
between i and j if there is an edge, and to infinity otherwise. Also, initialize dist[i][i]
to 0.
Iterative Update: For each intermediate vertex k (from 1 to N, where N is the number
of vertices), update dist[i][j] as min(dist[i][j], dist[i][k] + dist[k][j]).

Edge weights:
Edge from 0 to 1: 1
Edge from 0 to 2: 2
Edge from 1 to 2: 1
Edge from 1 to 3: 3
Edge from 2 to 3: 2

Implementation:

// C Program for Floyd Warshall Algorithm


#include <stdio.h>

// Number of vertices in the graph


#define V 4

/* Define Infinite as a large enough value. This value will be used for vertices not
connected to each other */
#define INF 99999

// A function to print the solution matrix


void printSolution(int dist[][V]);

// Solves the all-pairs shortest path


// problem using Floyd Warshall algorithm
void floydWarshall(int dist[][V])
{
int i, j, k;

for (k = 0; k < V; k++) {


// Pick all vertices as source one by one

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


// Pick all vertices as destination for the above picked source
for (j = 0; j < V; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}

// Print the shortest distance matrix


printSolution(dist);
}
/* A utility function to print solution */
void printSolution(int dist[][V])
{
printf( "The following matrix shows the shortest distances between every pair of
vertices \n");
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][j] == INF)
printf("%7s", "INF");
else
printf("%7d", dist[i][j]);
}
printf("\n");
}
}
// driver's code
int main()
{
int graph[V][V] = { { 0, 5, INF, 10 },
{ INF, 0, 3, INF },
{ INF, INF, 0, 1 },
{ INF, INF, INF, 0 } };

// Function call
floydWarshall(graph);
return 0;
}

Let us create the following weighted graph


10
(0)------->(3)
| /|\
5| |
| |1
\|/ |
(1)------->(2)
3
The transitive closure of a directed graph represents the reachability between pairs of
vertices in the graph. If there is a path from vertex i to vertex j in the graph, then
vertex j is reachable from vertex i, and this relationship is captured in the transitive
closure.

Importance
Helps in determining if there exists a path between every pair of vertices.
Useful in applications like compilers (dead code elimination), databases (query
optimization), and network routing.
Warshall's Algorithm
Warshall's algorithm is a dynamic programming approach used to compute the
transitive closure of a given directed graph.

Algorithm Steps:
Initialization: Create a matrix reach[V][V] where V is the number of vertices in the
graph. Initialize this matrix with the adjacency matrix of the graph.
Transitive Closure Computation: For each vertex k (from 0 to V-1), update reach[i][j]
as reach[i][j] || (reach[i][k] && reach[k][j]).

Implemenatation

#include <stdio.h>
#include <stdbool.h>

#define V 4

void printTransitiveClosure(bool reach[][V]) {


printf("Transitive closure of the given graph:\n");
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
printf("%d ", reach[i][j]);
}
printf("\n");
}
}

void transitiveClosure(int graph[V][V]) {


bool reach[V][V];

// Initialize the reach matrix with the graph matrix


for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
reach[i][j] = graph[i][j];
}
}

// Warshall's algorithm for transitive closure


for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
printTransitiveClosure(reach);
}

int main() {
int graph[V][V] = {
{1, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1}
};

transitiveClosure(graph);
return 0;
}
4. Design and implement C/C++ Program to find shortest paths from a given
vertex in a weighted connected graph to other vertices using Dijkstra's algorithm.

Algorithm Steps:
Initialization: Initialize a distance array dist[] to store the shortest distance from the
source vertex to each vertex. Initialize all distances as infinity (INT_MAX), except
for the source vertex which is 0.
Visited Set: Maintain a boolean array visited[] to mark vertices as visited or not.
Main Loop: Repeat the following steps until all vertices are visited:
Find the vertex u with the minimum distance dist[u] from the source vertex that has
not been visited yet.
Mark u as visited.
Update the distances of all adjacent vertices v of u if dist[u] + weight(u, v) < dist[v].

Example Problem
Given the following weighted graph, find the shortest paths from vertex 0 to all other
vertices using Dijkstra's algorithm:

There's a directed edge from vertex 0 to vertex 1 with a weight of 10.


There's a directed edge from vertex 0 to vertex 2 with a weight of 1.
There's a directed edge from vertex 1 to vertex 2 with a weight of 5.
There's a directed edge from vertex 2 to vertex 3 with a weight of 3.
There's a directed edge from vertex 1 to vertex 3 with a weight of 9.

Implementation

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

#define V 4

int minDistance(int dist[], bool visited[]) {


int min = INT_MAX, min_index;

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


if (visited[v] == false && dist[v] <= min) {
min = dist[v];
min_index = v;
}
}
return min_index;
}

void printSolution(int dist[]) {


printf("Vertex Distance from Source\n");
for (int i = 0; i < V; i++) {
printf("%d \t\t %d\n", i, dist[i]);
}
}
void dijkstra(int graph[V][V], int src) {
int dist[V];
bool visited[V];
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
visited[i] = false;
}

dist[src] = 0;
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, visited);
visited[u] = true;

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


if (!visited[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, 10, 0, 1},
{10, 0, 5, 0},
{0, 0, 0, 3},
{1, 5, 3, 0}
};

dijkstra(graph, 0);
return 0;
}
5. Design and implement C/C++ Program to obtain the Topological ordering of
vertices in a given digraph.

In a directed graph (digraph), a topological ordering of the vertices is a linear ordering


of the vertices such that for every directed edge (u, v), vertex u comes before v in the
ordering. A graph can have multiple topological orderings, and topological ordering is
only possible for Directed Acyclic Graphs (DAGs).

Importance:
Topological ordering is commonly used in:
Task scheduling
Dependency resolution
Program compilation (for resolving dependencies between modules)

Finding Topological Ordering Algorithms:


Depth-First Search (DFS):
Perform a DFS traversal and store vertices in a stack or list in the reverse order they
are finished.
This reverse order will be a valid topological ordering.

Kahn's Algorithm:
Use a queue to keep track of vertices with no incoming edges.
Remove vertices from the queue, update the incoming edges for its adjacent vertices,
and add them to the queue if they have no more incoming edges.

The vertices removed from the queue in the order they are removed form a valid
topological ordering.

Implementation

#include <stdio.h>
#include <stdlib.h>

#define MAX_VERTICES 100

struct Node {
int data;
struct Node* next;
};

struct Graph {
int numVertices;
struct Node** adjLists;
int* visited;
};

struct Node* createNode(int v) {


struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
newNode->data = v;
newNode->next = NULL;
return newNode;
}

struct Graph* createGraph(int vertices) {


struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph));
graph->numVertices = vertices;
graph->adjLists = (struct Node**) malloc(vertices * sizeof(struct Node*));
graph->visited = (int*) malloc(vertices * sizeof(int));

for (int i = 0; i < vertices; i++) {


graph->adjLists[i] = NULL;
graph->visited[i] = 0;
}
return graph;
}

void addEdge(struct Graph* graph, int src, int dest) {


struct Node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
}

void topologicalSortUtil(struct Graph* graph, int v, int* stack) {


graph->visited[v] = 1;

struct Node* adjList = graph->adjLists[v];


while (adjList != NULL) {
int connectedVertex = adjList->data;
if (!graph->visited[connectedVertex]) {
topologicalSortUtil(graph, connectedVertex, stack);
}
adjList = adjList->next;
}
stack[v] = 1;
}

void topologicalSort(struct Graph* graph) {


int* stack = (int*) malloc(graph->numVertices * sizeof(int));
for (int i = 0; i < graph->numVertices; i++) {
stack[i] = 0;
}

for (int i = 0; i < graph->numVertices; i++) {


if (!graph->visited[i]) {
topologicalSortUtil(graph, i, stack);
}
}

printf("Topological ordering of vertices: ");


for (int i = graph->numVertices - 1; i >= 0; i--) {
if (stack[i]) {
printf("%d ", i);
}
}
printf("\n");
free(stack);
}

int main() {
int vertices = 6; // Number of vertices in the graph
struct Graph* graph = createGraph(vertices);

addEdge(graph, 5, 0);
addEdge(graph, 5, 2);
addEdge(graph, 4, 0);
addEdge(graph, 4, 2);
addEdge(graph, 2, 3);
addEdge(graph, 3, 1);

topologicalSort(graph);
return 0;
}

You might also like