Skip to content

Commit 66cfd30

Browse files
authored
Merge branch 'master' into master
2 parents c4942a4 + ac65af4 commit 66cfd30

File tree

4 files changed

+473
-0
lines changed

4 files changed

+473
-0
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
7+
/**
8+
* This class implements Johnson's algorithm for finding all-pairs shortest paths in a weighted,
9+
* directed graph that may contain negative edge weights.
10+
*
11+
* Johnson's algorithm works by using the Bellman-Ford algorithm to compute a transformation of the
12+
* input graph that removes all negative weights, allowing Dijkstra's algorithm to be used for
13+
* efficient shortest path computations.
14+
*
15+
* Time Complexity: O(V^2 * log(V) + V*E)
16+
* Space Complexity: O(V^2)
17+
*
18+
* Where V is the number of vertices and E is the number of edges in the graph.
19+
*
20+
* For more information, please visit {@link https://en.wikipedia.org/wiki/Johnson%27s_algorithm}
21+
*/
22+
public final class JohnsonsAlgorithm {
23+
24+
// Constant representing infinity
25+
private static final double INF = Double.POSITIVE_INFINITY;
26+
27+
/**
28+
* A private constructor to hide the implicit public one.
29+
*/
30+
private JohnsonsAlgorithm() {
31+
}
32+
33+
/**
34+
* Executes Johnson's algorithm on the given graph.
35+
*
36+
* @param graph The input graph represented as an adjacency matrix.
37+
* @return A 2D array representing the shortest distances between all pairs of vertices.
38+
*/
39+
public static double[][] johnsonAlgorithm(double[][] graph) {
40+
int numVertices = graph.length;
41+
double[][] edges = convertToEdgeList(graph);
42+
43+
// Step 1: Add a new vertex and run Bellman-Ford
44+
double[] modifiedWeights = bellmanFord(edges, numVertices);
45+
46+
// Step 2: Reweight the graph
47+
double[][] reweightedGraph = reweightGraph(graph, modifiedWeights);
48+
49+
// Step 3: Run Dijkstra's algorithm for each vertex
50+
double[][] shortestDistances = new double[numVertices][numVertices];
51+
for (int source = 0; source < numVertices; source++) {
52+
shortestDistances[source] = dijkstra(reweightedGraph, source, modifiedWeights);
53+
}
54+
55+
return shortestDistances;
56+
}
57+
58+
/**
59+
* Converts the adjacency matrix representation of the graph to an edge list.
60+
*
61+
* @param graph The input graph as an adjacency matrix.
62+
* @return An array of edges, where each edge is represented as [from, to, weight].
63+
*/
64+
public static double[][] convertToEdgeList(double[][] graph) {
65+
int numVertices = graph.length;
66+
List<double[]> edgeList = new ArrayList<>();
67+
68+
for (int i = 0; i < numVertices; i++) {
69+
for (int j = 0; j < numVertices; j++) {
70+
if (i != j && !Double.isInfinite(graph[i][j])) {
71+
// Only add edges that are not self-loops and have a finite weight
72+
edgeList.add(new double[] {i, j, graph[i][j]});
73+
}
74+
}
75+
}
76+
77+
// Convert the List to a 2D array
78+
return edgeList.toArray(new double[0][]);
79+
}
80+
81+
/**
82+
* Implements the Bellman-Ford algorithm to compute the shortest paths from a new vertex
83+
* to all other vertices. This is used to calculate the weight function h(v) for reweighting.
84+
*
85+
* @param edges The edge list of the graph.
86+
* @param numVertices The number of vertices in the original graph.
87+
* @return An array of modified weights for each vertex.
88+
*/
89+
private static double[] bellmanFord(double[][] edges, int numVertices) {
90+
double[] dist = new double[numVertices + 1];
91+
Arrays.fill(dist, INF);
92+
dist[numVertices] = 0; // Distance to the new source vertex is 0
93+
94+
// Add edges from the new vertex to all original vertices
95+
double[][] allEdges = Arrays.copyOf(edges, edges.length + numVertices);
96+
for (int i = 0; i < numVertices; i++) {
97+
allEdges[edges.length + i] = new double[] {numVertices, i, 0};
98+
}
99+
100+
// Relax all edges V times
101+
for (int i = 0; i < numVertices; i++) {
102+
for (double[] edge : allEdges) {
103+
int u = (int) edge[0];
104+
int v = (int) edge[1];
105+
double weight = edge[2];
106+
if (dist[u] != INF && dist[u] + weight < dist[v]) {
107+
dist[v] = dist[u] + weight;
108+
}
109+
}
110+
}
111+
112+
// Check for negative weight cycles
113+
for (double[] edge : allEdges) {
114+
int u = (int) edge[0];
115+
int v = (int) edge[1];
116+
double weight = edge[2];
117+
if (dist[u] + weight < dist[v]) {
118+
throw new IllegalArgumentException("Graph contains a negative weight cycle");
119+
}
120+
}
121+
122+
return Arrays.copyOf(dist, numVertices);
123+
}
124+
125+
/**
126+
* Reweights the graph using the modified weights computed by Bellman-Ford.
127+
*
128+
* @param graph The original graph.
129+
* @param modifiedWeights The modified weights from Bellman-Ford.
130+
* @return The reweighted graph.
131+
*/
132+
public static double[][] reweightGraph(double[][] graph, double[] modifiedWeights) {
133+
int numVertices = graph.length;
134+
double[][] reweightedGraph = new double[numVertices][numVertices];
135+
136+
for (int i = 0; i < numVertices; i++) {
137+
for (int j = 0; j < numVertices; j++) {
138+
if (graph[i][j] != 0) {
139+
// New weight = original weight + h(u) - h(v)
140+
reweightedGraph[i][j] = graph[i][j] + modifiedWeights[i] - modifiedWeights[j];
141+
}
142+
}
143+
}
144+
145+
return reweightedGraph;
146+
}
147+
148+
/**
149+
* Implements Dijkstra's algorithm for finding shortest paths from a source vertex.
150+
*
151+
* @param reweightedGraph The reweighted graph to run Dijkstra's on.
152+
* @param source The source vertex.
153+
* @param modifiedWeights The modified weights from Bellman-Ford.
154+
* @return An array of shortest distances from the source to all other vertices.
155+
*/
156+
public static double[] dijkstra(double[][] reweightedGraph, int source, double[] modifiedWeights) {
157+
int numVertices = reweightedGraph.length;
158+
double[] dist = new double[numVertices];
159+
boolean[] visited = new boolean[numVertices];
160+
Arrays.fill(dist, INF);
161+
dist[source] = 0;
162+
163+
for (int count = 0; count < numVertices - 1; count++) {
164+
int u = minDistance(dist, visited);
165+
visited[u] = true;
166+
167+
for (int v = 0; v < numVertices; v++) {
168+
if (!visited[v] && reweightedGraph[u][v] != 0 && dist[u] != INF && dist[u] + reweightedGraph[u][v] < dist[v]) {
169+
dist[v] = dist[u] + reweightedGraph[u][v];
170+
}
171+
}
172+
}
173+
174+
// Adjust distances back to the original graph weights
175+
for (int i = 0; i < numVertices; i++) {
176+
if (dist[i] != INF) {
177+
dist[i] = dist[i] - modifiedWeights[source] + modifiedWeights[i];
178+
}
179+
}
180+
181+
return dist;
182+
}
183+
184+
/**
185+
* Finds the vertex with the minimum distance value from the set of vertices
186+
* not yet included in the shortest path tree.
187+
*
188+
* @param dist Array of distances.
189+
* @param visited Array of visited vertices.
190+
* @return The index of the vertex with minimum distance.
191+
*/
192+
public static int minDistance(double[] dist, boolean[] visited) {
193+
double min = INF;
194+
int minIndex = -1;
195+
for (int v = 0; v < dist.length; v++) {
196+
if (!visited[v] && dist[v] <= min) {
197+
min = dist[v];
198+
minIndex = v;
199+
}
200+
}
201+
return minIndex;
202+
}
203+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.thealgorithms.maths;
2+
3+
/**
4+
* This class provides a method to perform fast exponentiation (exponentiation by squaring),
5+
* which calculates (base^exp) % mod efficiently.
6+
*
7+
* <p>The algorithm works by repeatedly squaring the base and reducing the exponent
8+
* by half at each step. It exploits the fact that:
9+
* <ul>
10+
* <li>If exp is even, (base^exp) = (base^(exp/2))^2</li>
11+
* <li>If exp is odd, (base^exp) = base * (base^(exp-1))</li>
12+
* </ul>
13+
* The result is computed modulo `mod` at each step to avoid overflow and keep the result within bounds.
14+
* </p>
15+
*
16+
* <p><strong>Time complexity:</strong> O(log(exp)) — much faster than naive exponentiation (O(exp)).</p>
17+
*
18+
* For more information, please visit {@link https://en.wikipedia.org/wiki/Exponentiation_by_squaring}
19+
*/
20+
public final class FastExponentiation {
21+
22+
/**
23+
* Private constructor to hide the implicit public one.
24+
*/
25+
private FastExponentiation() {
26+
}
27+
28+
/**
29+
* Performs fast exponentiation to calculate (base^exp) % mod using the method
30+
* of exponentiation by squaring.
31+
*
32+
* <p>This method efficiently computes the result by squaring the base and halving
33+
* the exponent at each step. It multiplies the base to the result when the exponent is odd.
34+
*
35+
* @param base the base number to be raised to the power of exp
36+
* @param exp the exponent to which the base is raised
37+
* @param mod the modulus to ensure the result does not overflow
38+
* @return (base^exp) % mod
39+
* @throws IllegalArgumentException if the modulus is less than or equal to 0
40+
* @throws ArithmeticException if the exponent is negative (not supported in this implementation)
41+
*/
42+
public static long fastExponentiation(long base, long exp, long mod) {
43+
if (mod <= 0) {
44+
throw new IllegalArgumentException("Modulus must be positive.");
45+
}
46+
47+
if (exp < 0) {
48+
throw new ArithmeticException("Negative exponent is not supported.");
49+
}
50+
51+
long result = 1;
52+
base = base % mod; // Take the modulus of the base to handle large base values
53+
54+
// Fast exponentiation by squaring algorithm
55+
while (exp > 0) {
56+
// If exp is odd, multiply the base to the result
57+
if ((exp & 1) == 1) { // exp & 1 checks if exp is odd
58+
result = result * base % mod;
59+
}
60+
// Square the base and halve the exponent
61+
base = base * base % mod; // base^2 % mod to avoid overflow
62+
exp >>= 1; // Right shift exp to divide it by 2
63+
}
64+
65+
return result;
66+
}
67+
}

0 commit comments

Comments
 (0)