Skip to content

Commit 8b762e4

Browse files
authored
Merge branch 'master' into feat-stoer-wagner
2 parents 52385eb + 8b8434c commit 8b762e4

File tree

20 files changed

+2092
-205
lines changed

20 files changed

+2092
-205
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
- 📄 [TarjansAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java)
174174
- 📄 [UndirectedAdjacencyListGraph](src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java)
175175
- 📄 [WelshPowell](src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java)
176+
- 📄 [TwoSat](src/main/java/com/thealgorithms/datastructures/graphs/TwoSat.java)
176177
- 📁 **hashmap**
177178
- 📁 **hashing**
178179
- 📄 [GenericHashMapUsingArray](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java)
@@ -352,6 +353,7 @@
352353
- 📄 [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java)
353354
- 📄 [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java)
354355
- 📄 [TravelingSalesman](src/main/java/com/thealgorithms/graph/TravelingSalesman.java)
356+
- 📄 [Dinic](src/main/java/com/thealgorithms/graph/Dinic.java)
355357
- 📁 **greedyalgorithms**
356358
- 📄 [ActivitySelection](src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java)
357359
- 📄 [BandwidthAllocation](src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Set;
8+
9+
/**
10+
* An implementation of Dial's Algorithm for the single-source shortest path problem.
11+
* This algorithm is an optimization of Dijkstra's algorithm and is particularly
12+
* efficient for graphs with small, non-negative integer edge weights.
13+
*
14+
* It uses a bucket queue (implemented here as a List of HashSets) to store vertices,
15+
* where each bucket corresponds to a specific distance from the source. This is more
16+
* efficient than a standard priority queue when the range of edge weights is small.
17+
*
18+
* Time Complexity: O(E + W * V), where E is the number of edges, V is the number
19+
* of vertices, and W is the maximum weight of any edge.
20+
*
21+
* @see <a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Dial's_algorithm">Wikipedia - Dial's Algorithm</a>
22+
*/
23+
public final class DialsAlgorithm {
24+
/**
25+
* Private constructor to prevent instantiation of this utility class.
26+
*/
27+
private DialsAlgorithm() {
28+
}
29+
/**
30+
* Represents an edge in the graph, connecting to a destination vertex with a given weight.
31+
*/
32+
public static class Edge {
33+
private final int destination;
34+
private final int weight;
35+
36+
public Edge(int destination, int weight) {
37+
this.destination = destination;
38+
this.weight = weight;
39+
}
40+
41+
public int getDestination() {
42+
return destination;
43+
}
44+
45+
public int getWeight() {
46+
return weight;
47+
}
48+
}
49+
/**
50+
* Finds the shortest paths from a source vertex to all other vertices in a weighted graph.
51+
*
52+
* @param graph The graph represented as an adjacency list.
53+
* @param source The source vertex to start from (0-indexed).
54+
* @param maxEdgeWeight The maximum weight of any single edge in the graph.
55+
* @return An array of integers where the value at each index `i` is the
56+
* shortest distance from the source to vertex `i`. Unreachable vertices
57+
* will have a value of Integer.MAX_VALUE.
58+
* @throws IllegalArgumentException if the source vertex is out of bounds.
59+
*/
60+
public static int[] run(List<List<Edge>> graph, int source, int maxEdgeWeight) {
61+
int numVertices = graph.size();
62+
if (source < 0 || source >= numVertices) {
63+
throw new IllegalArgumentException("Source vertex is out of bounds.");
64+
}
65+
66+
// Initialize distances array
67+
int[] distances = new int[numVertices];
68+
Arrays.fill(distances, Integer.MAX_VALUE);
69+
distances[source] = 0;
70+
71+
// The bucket queue. Size is determined by the max possible path length.
72+
int maxPathWeight = maxEdgeWeight * (numVertices > 0 ? numVertices - 1 : 0);
73+
List<Set<Integer>> buckets = new ArrayList<>(maxPathWeight + 1);
74+
for (int i = 0; i <= maxPathWeight; i++) {
75+
buckets.add(new HashSet<>());
76+
}
77+
78+
// Add the source vertex to the first bucket
79+
buckets.get(0).add(source);
80+
81+
// Process buckets in increasing order of distance
82+
for (int d = 0; d <= maxPathWeight; d++) {
83+
// Process all vertices in the current bucket
84+
while (!buckets.get(d).isEmpty()) {
85+
// Get and remove a vertex from the current bucket
86+
int u = buckets.get(d).iterator().next();
87+
buckets.get(d).remove(u);
88+
89+
// If we've found a shorter path already, skip
90+
if (d > distances[u]) {
91+
continue;
92+
}
93+
94+
// Relax all adjacent edges
95+
for (Edge edge : graph.get(u)) {
96+
int v = edge.getDestination();
97+
int weight = edge.getWeight();
98+
99+
// If a shorter path to v is found
100+
if (distances[u] != Integer.MAX_VALUE && distances[u] + weight < distances[v]) {
101+
// If v was already in a bucket, remove it from the old one
102+
if (distances[v] != Integer.MAX_VALUE) {
103+
buckets.get(distances[v]).remove(v);
104+
}
105+
// Update distance and move v to the new bucket
106+
distances[v] = distances[u] + weight;
107+
buckets.get(distances[v]).add(v);
108+
}
109+
}
110+
}
111+
}
112+
return distances;
113+
}
114+
}
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.Stack;
6+
7+
/**
8+
* This class implements a solution to the 2-SAT (2-Satisfiability) problem
9+
* using Kosaraju's algorithm to find strongly connected components (SCCs)
10+
* in the implication graph.
11+
*
12+
* <p>
13+
* <strong>Brief Idea:</strong>
14+
* </p>
15+
*
16+
* <pre>
17+
* 1. From each clause (a ∨ b), we can derive implications:
18+
* (¬a → b) and (¬b → a)
19+
*
20+
* 2. We construct an implication graph using these implications.
21+
*
22+
* 3. For each variable x, its negation ¬x is also represented as a node.
23+
* If x and ¬x belong to the same SCC, the expression is unsatisfiable.
24+
*
25+
* 4. Otherwise, we assign truth values based on the SCC order:
26+
* If SCC(x) > SCC(¬x), then x = true; otherwise, x = false.
27+
* </pre>
28+
*
29+
* <p>
30+
* <strong>Complexities:</strong>
31+
* </p>
32+
* <ul>
33+
* <li>Time Complexity: O(n + m)</li>
34+
* <li>Space Complexity: O(n + m)</li>
35+
* </ul>
36+
* where {@code n} is the number of variables and {@code m} is the number of
37+
* clauses.
38+
*
39+
* <p>
40+
* <strong>Usage Example:</strong>
41+
* </p>
42+
*
43+
* <pre>
44+
* TwoSat twoSat = new TwoSat(5); // Initialize with 5 variables: x1, x2, x3, x4, x5
45+
*
46+
* // Add clauses
47+
* twoSat.addClause(1, false, 2, false); // (x1 ∨ x2)
48+
* twoSat.addClause(3, true, 2, false); // (¬x3 ∨ x2)
49+
* twoSat.addClause(4, false, 5, true); // (x4 ∨ ¬x5)
50+
*
51+
* twoSat.solve(); // Solve the problem
52+
*
53+
* if (twoSat.isSolutionExists()) {
54+
* boolean[] solution = twoSat.getSolutions();
55+
* for (int i = 1; i <= 5; i++) {
56+
* System.out.println("x" + i + " = " + solution[i]);
57+
* }
58+
* }
59+
* </pre>
60+
* <p><strong>Reference</strong></p>
61+
* <a href="https://cp-algorithms.com/graph/2SAT.html">CP Algorithm</a> <br></br>
62+
* <a href="https://en.wikipedia.org/wiki/2-satisfiability">Wikipedia - 2 SAT</a>
63+
* @author Shoyeb Ansari
64+
*
65+
* @see Kosaraju
66+
*/
67+
class TwoSat {
68+
69+
/** Number of variables in the boolean expression. */
70+
private final int numberOfVariables;
71+
72+
/** Implication graph built from the boolean clauses. */
73+
private final ArrayList<Integer>[] graph;
74+
75+
/** Transposed implication graph used in Kosaraju's algorithm. */
76+
private final ArrayList<Integer>[] graphTranspose;
77+
78+
/** Stores one valid truth assignment for all variables (1-indexed). */
79+
private final boolean[] variableAssignments;
80+
81+
/** Indicates whether a valid solution exists. */
82+
private boolean hasSolution = true;
83+
84+
/** Tracks whether the {@code solve()} method has been called. */
85+
private boolean isSolved = false;
86+
87+
/**
88+
* Initializes the TwoSat solver with the given number of variables.
89+
*
90+
* @param numberOfVariables the number of boolean variables
91+
* @throws IllegalArgumentException if the number of variables is negative
92+
*/
93+
@SuppressWarnings({"unchecked", "rawtypes"})
94+
TwoSat(int numberOfVariables) {
95+
if (numberOfVariables < 0) {
96+
throw new IllegalArgumentException("Number of variables cannot be negative.");
97+
}
98+
this.numberOfVariables = numberOfVariables;
99+
int n = 2 * numberOfVariables + 1;
100+
101+
graph = (ArrayList<Integer>[]) new ArrayList[n];
102+
graphTranspose = (ArrayList<Integer>[]) new ArrayList[n];
103+
for (int i = 0; i < n; i++) {
104+
graph[i] = new ArrayList<>();
105+
graphTranspose[i] = new ArrayList<>();
106+
}
107+
variableAssignments = new boolean[numberOfVariables + 1];
108+
}
109+
110+
/**
111+
* Adds a clause of the form (a ∨ b) to the boolean expression.
112+
*
113+
* <p>
114+
* Example: To add (¬x₁ ∨ x₂), call:
115+
* </p>
116+
*
117+
* <pre>{@code
118+
* addClause(1, true, 2, false);
119+
* }</pre>
120+
*
121+
* @param a the first variable (1 ≤ a ≤ numberOfVariables)
122+
* @param isNegateA {@code true} if variable {@code a} is negated
123+
* @param b the second variable (1 ≤ b ≤ numberOfVariables)
124+
* @param isNegateB {@code true} if variable {@code b} is negated
125+
* @throws IllegalArgumentException if {@code a} or {@code b} are out of range
126+
*/
127+
void addClause(int a, boolean isNegateA, int b, boolean isNegateB) {
128+
if (a <= 0 || a > numberOfVariables) {
129+
throw new IllegalArgumentException("Variable number must be between 1 and " + numberOfVariables);
130+
}
131+
if (b <= 0 || b > numberOfVariables) {
132+
throw new IllegalArgumentException("Variable number must be between 1 and " + numberOfVariables);
133+
}
134+
135+
a = isNegateA ? negate(a) : a;
136+
b = isNegateB ? negate(b) : b;
137+
int notA = negate(a);
138+
int notB = negate(b);
139+
140+
// Add implications: (¬a → b) and (¬b → a)
141+
graph[notA].add(b);
142+
graph[notB].add(a);
143+
144+
// Build transpose graph
145+
graphTranspose[b].add(notA);
146+
graphTranspose[a].add(notB);
147+
}
148+
149+
/**
150+
* Solves the 2-SAT problem using Kosaraju's algorithm to find SCCs
151+
* and determines whether a satisfying assignment exists.
152+
*/
153+
void solve() {
154+
isSolved = true;
155+
int n = 2 * numberOfVariables + 1;
156+
157+
boolean[] visited = new boolean[n];
158+
int[] component = new int[n];
159+
Stack<Integer> topologicalOrder = new Stack<>();
160+
161+
// Step 1: Perform DFS to get topological order
162+
for (int i = 1; i < n; i++) {
163+
if (!visited[i]) {
164+
dfsForTopologicalOrder(i, visited, topologicalOrder);
165+
}
166+
}
167+
168+
Arrays.fill(visited, false);
169+
int sccId = 0;
170+
171+
// Step 2: Find SCCs on transposed graph
172+
while (!topologicalOrder.isEmpty()) {
173+
int node = topologicalOrder.pop();
174+
if (!visited[node]) {
175+
dfsForScc(node, visited, component, sccId);
176+
sccId++;
177+
}
178+
}
179+
180+
// Step 3: Check for contradictions and assign values
181+
for (int i = 1; i <= numberOfVariables; i++) {
182+
int notI = negate(i);
183+
if (component[i] == component[notI]) {
184+
hasSolution = false;
185+
return;
186+
}
187+
// If SCC(i) > SCC(¬i), then variable i is true.
188+
variableAssignments[i] = component[i] > component[notI];
189+
}
190+
}
191+
192+
/**
193+
* Returns whether the given boolean formula is satisfiable.
194+
*
195+
* @return {@code true} if a solution exists; {@code false} otherwise
196+
* @throws Error if called before {@link #solve()}
197+
*/
198+
boolean isSolutionExists() {
199+
if (!isSolved) {
200+
throw new Error("Please call solve() before checking for a solution.");
201+
}
202+
return hasSolution;
203+
}
204+
205+
/**
206+
* Returns one valid assignment of variables that satisfies the boolean formula.
207+
*
208+
* @return a boolean array where {@code result[i]} represents the truth value of
209+
* variable {@code xᵢ}
210+
* @throws Error if called before {@link #solve()} or if no solution exists
211+
*/
212+
boolean[] getSolutions() {
213+
if (!isSolved) {
214+
throw new Error("Please call solve() before fetching the solution.");
215+
}
216+
if (!hasSolution) {
217+
throw new Error("No satisfying assignment exists for the given expression.");
218+
}
219+
return variableAssignments.clone();
220+
}
221+
222+
/** Performs DFS to compute topological order. */
223+
private void dfsForTopologicalOrder(int u, boolean[] visited, Stack<Integer> topologicalOrder) {
224+
visited[u] = true;
225+
for (int v : graph[u]) {
226+
if (!visited[v]) {
227+
dfsForTopologicalOrder(v, visited, topologicalOrder);
228+
}
229+
}
230+
topologicalOrder.push(u);
231+
}
232+
233+
/** Performs DFS on the transposed graph to identify SCCs. */
234+
private void dfsForScc(int u, boolean[] visited, int[] component, int sccId) {
235+
visited[u] = true;
236+
component[u] = sccId;
237+
for (int v : graphTranspose[u]) {
238+
if (!visited[v]) {
239+
dfsForScc(v, visited, component, sccId);
240+
}
241+
}
242+
}
243+
244+
/**
245+
* Returns the index representing the negation of the given variable.
246+
*
247+
* <p>
248+
* Mapping rule:
249+
* </p>
250+
*
251+
* <pre>
252+
* For a variable i:
253+
* negate(i) = i + n
254+
* For a negated variable (i + n):
255+
* negate(i + n) = i
256+
* where n = numberOfVariables
257+
* </pre>
258+
*
259+
* @param a the variable index
260+
* @return the index representing its negation
261+
*/
262+
private int negate(int a) {
263+
return a <= numberOfVariables ? a + numberOfVariables : a - numberOfVariables;
264+
}
265+
}

0 commit comments

Comments
 (0)