Skip to content

Commit

Permalink
Implemented the graph algorithms in C++ Issue strivedi4u#216
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhik004 committed Oct 10, 2024
1 parent 3008b2e commit c84d057
Show file tree
Hide file tree
Showing 7 changed files with 643 additions and 131 deletions.
79 changes: 79 additions & 0 deletions Graph Algorithms/Bellman-Ford.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* BELLMAN-FORD ALGORITHM
Intuition -
The Bellman-Ford algorithm is used to find the shortest path from a single source to all other vertices in a graph, even if the graph contains negative-weight edges. The key idea is to iteratively relax all edges multiple times and update the shortest distance to each vertex. If after V-1 iterations (where V is the number of vertices) there is still an edge that can be relaxed, it indicates the presence of a negative-weight cycle.
Approach -
Initialization:
Set the distance to the source vertex S as 0 and all other vertices to a large value (representing infinity).
Relaxation:
For each vertex, repeat the process of checking every edge. If the distance to the destination vertex v through an edge (u, v) is shorter than the previously known distance, update the distance of v.
Detect Negative Cycle:
After performing V-1 iterations (where V is the number of vertices), perform one more iteration over all edges. If a distance can still be updated, it means there's a negative-weight cycle, and the algorithm returns -1 to indicate that.
Return Result:
If no negative cycle is found, return the shortest distance for all vertices.
This algorithm runs in O(V * E), where V is the number of vertices and E is the number of edges.*/

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
vector<int> bellman_ford(int V, vector<vector<int>>& edges, int S) {
vector<int> distance(V, 100000000);
distance[S] = 0;

for (int i = 0; i < V - 1; i++) {
for (auto& adj : edges) {
int u = adj[0];
int v = adj[1];
int weight = adj[2];
if (distance[u] != 100000000 && distance[u] + weight < distance[v]) {
distance[v] = distance[u] + weight;
}
}
}

for (int i = 0; i < V; i++) {
for (auto& adj : edges) {
int u = adj[0];
int v = adj[1];
int weight = adj[2];
if (distance[u] != 100000000 && distance[u] + weight < distance[v]) {
return {-1};
}
}
}

return distance;
}
};

int main() {
int V, E, S;
cin >> V >> E >> S;

vector<vector<int>> edges(E);
for (int i = 0; i < E; i++) {
int u, v, w;
cin >> u >> v >> w;
edges[i] = {u, v, w};
}

Solution sol;
vector<int> result = sol.bellman_ford(V, edges, S);

if (result[0] == -1) {
cout << "Negative weight cycle detected" << endl;
} else {
for (int i = 0; i < V; i++) {
cout << "Shortest distance to vertex " << i << ": " << result[i] << endl;
}
}

return 0;
}
133 changes: 133 additions & 0 deletions Graph Algorithms/Djikstra.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* DJIKSTRA ALGORITHM
Intuition -
Dijkstra's algorithm is used to find the shortest path from a source vertex to all other vertices in a graph. The algorithm works by iteratively selecting the node with the minimum tentative distance (weight) and exploring its neighbors, updating their distances if a shorter path is found through the selected node.
In this specific implementation:
We need to maintain a visited array to track which nodes have already been processed.
The weight array stores the shortest known distance from the source to each node. Initially, all distances are set to infinity (INT_MAX) except for the source node, which is set to 0.
We need to repeatedly select the unvisited node with the minimum weight (using the getMinWeightNode function) and update the weights of its neighbors if a shorter path is found.
The process continues until all nodes have been visited and their shortest distances from the source are determined.
Approach-
Data Structures:
visited[]: An array that keeps track of whether a node has already been processed.
weight[]: An array that holds the current shortest distance from the source to each node.
mp: A map that represents the graph, where each node points to a list of pairs. Each pair contains a neighboring node and the weight of the edge connecting them.
Initialization:
Set all distances to infinity (INT_MAX) except for the source node, which is initialized to 0.
The visited array is initialized to all 0s (i.e., no node has been visited yet).
Main Loop:
In each iteration, find the unvisited node with the minimum weight using the getMinWeightNode function.
For the selected node, check each of its neighbors. If the sum of the current node's weight and the edge weight is less than the known weight of the neighbor, update the neighbor's weight.
Mark the current node as visited and repeat until all nodes are processed.
Helper Function:
The getMinWeightNode function iterates over all nodes to find the unvisited node with the smallest weight.
Final Output:
Once the algorithm finishes, the weight[] array will contain the shortest distances from the source node to all other nodes, which is returned as the result.
Detailed Steps in the Code :
getMinWeightNode: This function iterates through the weight[] array to find the unvisited node with the minimum weight and returns it.
dijkstra_helper: This is the main Dijkstra algorithm implementation:
It initializes the visited[] and weight[] arrays.
It then repeatedly selects the node with the smallest weight (using getMinWeightNode), explores its neighbors, and updates their weights if a shorter path is found.
Once all nodes have been processed, it returns the weight[] array as a vector.
dijkstra: This function converts the adjacency list representation into the map<int, vector<pair<int,int>>> format used in the dijkstra_helper function. It then calls dijkstra_helper to get the shortest path result.*/

#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <climits>
using namespace std;

class Solution {
public:
int getMinWeightNode(int* weight, int* visited, int n) {
int min_node = -1;
int min_val = INT_MAX;
for (int i = 0; i < n; i++) {
if (!visited[i] && min_val > weight[i]) {
min_node = i;
min_val = weight[i];
}
}
return min_node;
}

vector<int> dijkstra_helper(map<int, vector<pair<int,int>>>& mp, int n, int s) {
int visited[n];
int weight[n];
memset(visited, 0, sizeof(visited));
for (int i = 0; i < n; i++) {
weight[i] = INT_MAX;
}

weight[s] = 0;
int count = n;
while (count) {
int node = getMinWeightNode(weight, visited, n);
if (node == -1) break;
for (int i = 0; i < mp[node].size(); i++) {
int neighbor = mp[node][i].first;
int edge_weight = mp[node][i].second;
if (!visited[neighbor] && (edge_weight + weight[node] < weight[neighbor])) {
weight[neighbor] = edge_weight + weight[node];
}
}
visited[node] = 1;
count--;
}

vector<int> ans;
for (int i = 0; i < n; i++) {
ans.push_back(weight[i]);
}
return ans;
}

vector<int> dijkstra(int V, vector<vector<int>> adj[], int S) {
map<int, vector<pair<int,int>>> mp;
for (int i = 0; i < V; i++) {
for (int j = 0; j < adj[i].size(); j++) {
int vertex = adj[i][j][0];
int weight = adj[i][j][1];
mp[i].push_back({vertex, weight});
}
}
return dijkstra_helper(mp, V, S);
}
};

int main() {
int V, E, source;
cin >> V >> E;

vector<vector<int>> adj[V];

for (int i = 0; i < E; i++) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}

cin >> source;

Solution sol;
vector<int> result = sol.dijkstra(V, adj, source);

for (int i = 0; i < result.size(); i++) {
cout << "Shortest distance to vertex " << i << ": " << result[i] << endl;
}

return 0;
}
67 changes: 67 additions & 0 deletions Graph Algorithms/Floyd-Warshall.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* FLOYD WARSHALL'S ALGORITHM
Intuition -
The Floyd-Warshall algorithm is used to find the shortest path between all pairs of vertices in a graph. The algorithm works by iteratively improving the path between two vertices by introducing an intermediate vertex. If a shorter path exists through this intermediate vertex, the current shortest path is updated. This approach allows it to handle both positive and negative edge weights (as long as there are no negative-weight cycles).
Approach -
Initialization:
Replace all occurrences of -1 in the matrix with a large value (INF) to represent that there is no direct path between the vertices.
Relaxation using an Intermediate Vertex:
For each pair of vertices (i, j), check if going through another vertex k offers a shorter path from i to j. If matrix[i][j] can be reduced by taking the path through k (matrix[i][k] + matrix[k][j]), update matrix[i][j].
Restoration:
After processing, revert any distance that remains INF back to -1, meaning no path exists between those vertices.
Time Complexity:
The algorithm runs in O(n³), where n is the number of vertices in the graph.*/

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
void shortest_distance(vector<vector<int>>& matrix) {
int n = matrix.size();
const int INF = 1e8;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == -1) matrix[i][j] = INF;
}
}
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
if (matrix[i][k] == INF) continue;
for (int j = 0; j < n; j++) {
if (matrix[k][j] == INF) continue;
matrix[i][j] = min(matrix[i][j], matrix[i][k] + matrix[k][j]);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == INF) matrix[i][j] = -1;
}
}
}
};

int main() {
int n;
cin >> n;
vector<vector<int>> matrix(n, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> matrix[i][j];
}
}
Solution sol;
sol.shortest_distance(matrix);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
return 0;
}
72 changes: 72 additions & 0 deletions Graph Algorithms/KosaRaju.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* KOSARAJU ALGORITHM
Intuition-
The goal is to find substrings that do not overlap, while ensuring that all occurrences of each character are included in their respective substring.
For example, consider the string "abcdade".
The substring "abcda" is invalid because not all occurrences of the character d are included.
The substring "abcdad" is valid, but if we choose this, the result will be two substrings: "abcdad" and "e". This gives us two substrings.
However, if we take "b", "c", and "e" as separate substrings, all of these are valid. This approach results in three substrings, which is the correct answer for this case.
Approach -
In the first loop, I record the starting and ending positions of each character. Specifically:
The start is the leftmost occurrence of the character.
The end is the rightmost occurrence of the character.
The detailed explanation is provided in the comments within the code.*/

#include <iostream>
#include <vector>
#include <string>
#include <climits>
using namespace std;

class Solution {
public:
vector<string> maxNumOfSubstrings(string s) {
vector<int> start(26, -1);
vector<int> end(26, 0);
vector<int> invalid(26, 0);
vector<string> result;

for (int i = 0; i < s.size(); i++) {
int idx = s[i] - 'a';
if (start[idx] == -1) start[idx] = i;
end[idx] = i;
}

for (int i = 0; i < 26; i++) {
if (start[i] == -1) continue;
for (int k = start[i]; k <= end[i]; k++) {
if (start[s[k] - 'a'] < start[i]) {
invalid[i] = 1;
break;
} else end[i] = max(end[i], end[s[k] - 'a']);
}
}

int lastCut = INT_MAX;
for (int i = s.size() - 1; i >= 0; i--) {
if (i == start[s[i] - 'a'] && end[s[i] - 'a'] < lastCut && !invalid[s[i] - 'a']) {
result.push_back(s.substr(i, end[s[i] - 'a'] - i + 1));
lastCut = i;
}
}

return result;
}
};

int main() {
Solution sol;
string input = "abcdade";

vector<string> result = sol.maxNumOfSubstrings(input);

cout << "Maximum number of substrings: " << result.size() << endl;
for (const string& substr : result) {
cout << substr << endl;
}

return 0;
}
Loading

0 comments on commit c84d057

Please sign in to comment.