Table of contents
Welcome to Day 25 of my 100 Days of DSA challenge! Today, I explored advanced graph algorithms, including Dijkstra’s for shortest paths, Kruskal’s and Prim’s for minimum spanning trees, and topological sorting. These algorithms are essential for solving weighted graph problems and designing efficient networks. 🤖✨
Check out my GitHub repository for all the solutions and progress updates at: 100 Days of DSA
Let’s keep learning! 🚀✨
1. Dijkstra's Algorithm for Finding the Shortest Path in a Weighted Graph
This program implements Dijkstra's algorithm for finding the shortest path in a weighted graph. The graph is represented using an adjacency matrix, and the algorithm uses an array to track distances and visited vertices. It repeatedly selects the vertex with the smallest distance and updates the distances of its neighbors.
Code:
#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
#define MAX 100 // Maximum number of vertices in the graph
class Graph {
private:
int adj_matrix[MAX][MAX]; // Adjacency matrix to store edge weights
int num_vertices; // Number of vertices in the graph
public:
// Constructor to initialize the graph
Graph(int vertices) {
num_vertices = vertices;
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
adj_matrix[i][j] = (i == j) ? 0 : INT_MAX; // No edge between nodes initially
}
}
}
// Add edge to the graph
void add_edge(int src, int dest, int weight) {
adj_matrix[src][dest] = weight;
}
// Function to find the vertex with the minimum distance that is not yet processed
int find_min_distance(int distance[], bool visited[]) {
int min_distance = INT_MAX;
int min_index = -1;
for (int i = 0; i < num_vertices; i++) {
if (!visited[i] && distance[i] < min_distance) {
min_distance = distance[i];
min_index = i;
}
}
return min_index;
}
// Dijkstra's algorithm to find the shortest path from a source vertex
void dijkstra(int source) {
int distance[MAX]; // Array to store shortest distance from source to each vertex
bool visited[MAX]; // Array to mark visited vertices
// Initialize distances to infinity and visited array to false
for (int i = 0; i < num_vertices; i++) {
distance[i] = INT_MAX;
visited[i] = false;
}
distance[source] = 0; // Distance from source to itself is 0
// Process each vertex
for (int count = 0; count < num_vertices - 1; count++) {
int u = find_min_distance(distance, visited);
if (u == -1) break; // If no vertex is reachable, break
visited[u] = true;
// Update distance for all adjacent vertices of the chosen vertex
for (int v = 0; v < num_vertices; v++) {
if (!visited[v] && adj_matrix[u][v] != INT_MAX && distance[u] + adj_matrix[u][v] < distance[v]) {
distance[v] = distance[u] + adj_matrix[u][v];
}
}
}
// Print the shortest distances
cout << "Vertex\tDistance from Source" << endl;
for (int i = 0; i < num_vertices; i++) {
cout << i << "\t" << (distance[i] == INT_MAX ? -1 : distance[i]) << endl;
}
}
};
int main() {
int vertices = 6;
Graph graph(vertices);
graph.add_edge(0, 1, 4);
graph.add_edge(0, 2, 2);
graph.add_edge(1, 2, 5);
graph.add_edge(1, 3, 10);
graph.add_edge(2, 4, 3);
graph.add_edge(4, 3, 4);
graph.add_edge(3, 5, 11);
int source = 0;
cout << "Dijkstra's Algorithm (Source: " << source << ")" << endl;
graph.dijkstra(source);
return 0;
}
Output:
2. Network Delay Time Problem
This program implements the "Network Delay Time" problem using Dijkstra's algorithm. It calculates the minimum time required for a signal to travel from the source node to all other nodes in a weighted graph. If some nodes are unreachable, it reports so; otherwise, it outputs the maximum delay. The graph is represented with an adjacency matrix, and the algorithm finds the shortest path iteratively.
Code:
#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
#define MAX 100 // Maximum number of vertices in the graph
class NetworkDelay {
private:
int adj_matrix[MAX][MAX]; // Adjacency matrix to store edge weights
int num_vertices; // Number of vertices in the graph
public:
// Constructor to initialize the graph
NetworkDelay(int vertices) {
num_vertices = vertices;
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
adj_matrix[i][j] = (i == j) ? 0 : INT_MAX; // No edge between nodes initially
}
}
}
// Add edge to the graph
void add_edge(int src, int dest, int weight) {
adj_matrix[src][dest] = weight;
}
// Function to find the vertex with the minimum distance that is not yet processed
int find_min_distance(int distance[], bool visited[]) {
int min_distance = INT_MAX;
int min_index = -1;
for (int i = 0; i < num_vertices; i++) {
if (!visited[i] && distance[i] < min_distance) {
min_distance = distance[i];
min_index = i;
}
}
return min_index;
}
// Dijkstra's algorithm to calculate network delay time
int network_delay(int source) {
int distance[MAX]; // Array to store shortest distance from source to each vertex
bool visited[MAX]; // Array to mark visited vertices
// Initialize distances to infinity and visited array to false
for (int i = 0; i < num_vertices; i++) {
distance[i] = INT_MAX;
visited[i] = false;
}
distance[source] = 0; // Distance from source to itself is 0
// Process each vertex
for (int count = 0; count < num_vertices - 1; count++) {
int u = find_min_distance(distance, visited);
if (u == -1) break; // If no vertex is reachable, break
visited[u] = true;
// Update distance for all adjacent vertices of the chosen vertex
for (int v = 0; v < num_vertices; v++) {
if (!visited[v] && adj_matrix[u][v] != INT_MAX && distance[u] + adj_matrix[u][v] < distance[v]) {
distance[v] = distance[u] + adj_matrix[u][v];
}
}
}
// Find the maximum distance from the source
int max_distance = 0;
for (int i = 0; i < num_vertices; i++) {
if (distance[i] == INT_MAX) {
return -1; // If any vertex is unreachable
}
max_distance = max(max_distance, distance[i]);
}
return max_distance;
}
};
int main() {
int vertices = 4;
NetworkDelay network(vertices);
network.add_edge(0, 1, 1);
network.add_edge(0, 2, 3);
network.add_edge(1, 2, 1);
network.add_edge(2, 3, 1);
int source = 0;
int result = network.network_delay(source);
if (result == -1) {
cout << "Not all nodes are reachable from the source." << endl;
} else {
cout << "The network delay time is: " << result << " units." << endl;
}
return 0;
}
Output:
3. Kruskal's Algorithm to Find the Minimum Spanning Tree of a Graph
This program implements Kruskal's algorithm to find the Minimum Spanning Tree (MST) of a weighted, undirected graph. It stores the graph's edges and sorts them by weight. A disjoint set (union-find) is used to detect cycles and ensure edges added to the MST do not form a cycle. Each edge is processed in order of increasing weight, and if it connects two disjoint sets, it is added to the MST. The program outputs the edges of the MST and its total weight.
Code:
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 100 // Maximum number of vertices in the graph
class Kruskal {
private:
int parent[MAX]; // Array to represent the disjoint set
int edges[MAX][3]; // Array to store the edges (u, v, weight)
int num_edges; // Number of edges in the graph
int num_vertices; // Number of vertices in the graph
public:
// Constructor to initialize the graph
Kruskal(int vertices) {
num_vertices = vertices;
num_edges = 0;
}
// Add an edge to the graph
void add_edge(int u, int v, int weight) {
edges[num_edges][0] = u;
edges[num_edges][1] = v;
edges[num_edges][2] = weight;
num_edges++;
}
// Function to find the parent of a node in the disjoint set
int find_parent(int node) {
if (parent[node] == -1) {
return node;
}
return parent[node] = find_parent(parent[node]); // Path compression
}
// Function to perform union of two sets
void union_sets(int u, int v) {
int parent_u = find_parent(u);
int parent_v = find_parent(v);
parent[parent_u] = parent_v;
}
// Manual sorting function for the edges based on weight
void sort_edges() {
for (int i = 0; i < num_edges - 1; i++) {
for (int j = 0; j < num_edges - i - 1; j++) {
if (edges[j][2] > edges[j + 1][2]) {
// Swap edges[j] and edges[j + 1]
for (int k = 0; k < 3; k++) {
int temp = edges[j][k];
edges[j][k] = edges[j + 1][k];
edges[j + 1][k] = temp;
}
}
}
}
}
// Function to find the MST using Kruskal's algorithm
void find_mst() {
sort_edges(); // Sort edges by weight
memset(parent, -1, sizeof(parent)); // Initialize disjoint set
int mst_weight = 0; // Total weight of MST
cout << "Edges in the Minimum Spanning Tree:" << endl;
for (int i = 0; i < num_edges; i++) {
int u = edges[i][0];
int v = edges[i][1];
int weight = edges[i][2];
// Check if adding this edge forms a cycle
if (find_parent(u) != find_parent(v)) {
cout << u << " -- " << v << " == " << weight << endl;
mst_weight += weight;
union_sets(u, v);
}
}
cout << "Total weight of MST: " << mst_weight << endl;
}
};
int main() {
int vertices = 5;
Kruskal graph(vertices);
graph.add_edge(0, 1, 10);
graph.add_edge(0, 2, 6);
graph.add_edge(0, 3, 5);
graph.add_edge(1, 3, 15);
graph.add_edge(2, 3, 4);
graph.find_mst();
return 0;
}
Output:
4. Prim’s Algorithm to Find Minimum Cost to Connect All Points
This program implements Prim's algorithm to find the minimum cost to connect all points given their coordinates. It first initializes an adjacency matrix to store the distances (Manhattan distances) between each pair of points. Then, it uses Prim's algorithm to compute the minimum spanning tree (MST) by selecting edges with the smallest weight that connect unvisited points, ensuring all points are connected with the least total cost. The final result is the minimum cost to connect all points, which is printed at the end.
Code:
#include <iostream>
#include <climits>
#include <cmath>
using namespace std;
#define MAX 100 // Maximum number of points
class MinCostConnectPoints {
private:
int graph[MAX][MAX]; // Adjacency matrix for storing distances
int num_points; // Number of points
public:
// Constructor to initialize the graph
MinCostConnectPoints(int points) {
num_points = points;
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
graph[i][j] = 0; // No connections initially
}
}
}
// Function to add edges between points with weights
void add_edge(int src, int dest, int weight) {
graph[src][dest] = weight;
graph[dest][src] = weight; // Undirected graph
}
// Find the point with the minimum key value not included in MST
int find_min_key(int key[], bool in_mst[]) {
int min_key = INT_MAX;
int min_index = -1;
for (int i = 0; i < num_points; i++) {
if (!in_mst[i] && key[i] < min_key) {
min_key = key[i];
min_index = i;
}
}
return min_index;
}
// Prim's algorithm to find the minimum cost to connect all points
int find_min_cost() {
int key[MAX]; // Minimum weight edge to include each vertex
bool in_mst[MAX]; // True if vertex is included in MST
int total_cost = 0;
// Initialize keys as infinite and in_mst[] as false
for (int i = 0; i < num_points; i++) {
key[i] = INT_MAX;
in_mst[i] = false;
}
key[0] = 0; // Start with the first point
// Iterate to form the MST
for (int count = 0; count < num_points; count++) {
int u = find_min_key(key, in_mst);
if (u == -1) break; // No more reachable points
in_mst[u] = true; // Include this point in MST
total_cost += key[u];
// Update the key values of the adjacent points
for (int v = 0; v < num_points; v++) {
if (!in_mst[v] && graph[u][v] != 0 && graph[u][v] < key[v]) {
key[v] = graph[u][v];
}
}
}
return total_cost;
}
};
int main() {
int points = 4;
MinCostConnectPoints min_cost(points);
// Define the points and calculate the Manhattan distances
int coordinates[4][2] = {{0, 0}, {2, 2}, {3, 10}, {5, 2}};
for (int i = 0; i < points; i++) {
for (int j = i + 1; j < points; j++) {
int weight = abs(coordinates[i][0] - coordinates[j][0]) + abs(coordinates[i][1] - coordinates[j][1]);
min_cost.add_edge(i, j, weight);
}
}
int result = min_cost.find_min_cost();
cout << "The minimum cost to connect all points is: " << result << endl;
return 0;
}
Output:
5. Solve the "alien dictionary" problem using topological sorting.
This program solves the "Alien Dictionary" problem by determining the order of characters in an alien language based on the given list of words. It constructs a directed graph where each character is a node, and edges represent the precedence of characters as inferred from consecutive words. The graph is then processed using topological sorting to determine a valid character order. If a cycle is detected in the graph (indicating no valid order), it returns an empty string. Otherwise, it outputs the topologically sorted order of characters. The solution uses BFS for topological sorting via Kahn's algorithm.
Code:
#include <iostream>
#include <climits>
#include <queue>
#include <cstring>
using namespace std;
#define MAX 26 // As there are only 26 lowercase English letters
class AlienDictionary {
private:
int in_degree[MAX]; // In-degree of each node
char graph[MAX][MAX]; // Adjacency matrix for the graph
int num_chars; // Number of characters in the alphabet
public:
// Constructor to initialize graph and in-degree
AlienDictionary(int n) {
num_chars = n;
memset(in_degree, 0, sizeof(in_degree)); // Initialize in-degree to 0
memset(graph, 0, sizeof(graph)); // Initialize graph with no edges
}
// Function to add an edge from 'u' to 'v'
void add_edge(char u, char v) {
int u_idx = u - 'a';
int v_idx = v - 'a';
if (graph[u_idx][v_idx] == 0) {
graph[u_idx][v_idx] = 1; // There is a directed edge from u to v
in_degree[v_idx]++; // Increase the in-degree of 'v'
}
}
// Function to perform topological sort and return the result
string topological_sort() {
queue<int> q;
string result = "";
// Add nodes with 0 in-degree to the queue
for (int i = 0; i < num_chars; i++) {
if (in_degree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
result += (char)(u + 'a');
// Decrease in-degree of neighbors and add them to the queue if in-degree becomes 0
for (int v = 0; v < num_chars; v++) {
if (graph[u][v] == 1) {
in_degree[v]--;
if (in_degree[v] == 0) {
q.push(v);
}
}
}
}
// If all characters are included in the result, return the result
if (result.length() == num_chars) {
return result;
} else {
return ""; // Return an empty string if there is a cycle
}
}
// Function to build the graph from the list of words
void build_graph(string words[], int word_count) {
for (int i = 0; i < word_count - 1; i++) {
string word1 = words[i];
string word2 = words[i + 1];
// Find the first different character between word1 and word2
int len = min(word1.length(), word2.length());
for (int j = 0; j < len; j++) {
if (word1[j] != word2[j]) {
add_edge(word1[j], word2[j]);
break;
}
}
}
}
};
int main() {
string words[] = {"z", "x", "z", "z", "x"};
int word_count = 5;
// There are 26 possible lowercase characters
AlienDictionary alienDict(26);
// Build the graph from the list of words
alienDict.build_graph(words, word_count);
// Get the topological order of characters
string order = alienDict.topological_sort();
if (order == "") {
cout << "There is a cycle in the graph, no valid order possible." << endl;
} else {
cout << "The order of characters in the alien language is: " << order << endl;
}
return 0;
}
Output:
Day 25 highlighted the power of advanced graph algorithms in solving complex, real-world problems like optimizing networks, calculating shortest paths, and handling dynamic systems. Understanding these algorithms is crucial for building scalable and efficient solutions in various domains, from telecommunications to AI. 🚀