Skip to content

Commit ec08373

Browse files
williamfisetclaude
andauthored
Refactor TarjanSccSolver, remove TarjanAdjacencyMatrix (#1307)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 96ec0eb commit ec08373

File tree

3 files changed

+43
-175
lines changed

3 files changed

+43
-175
lines changed

src/main/java/com/williamfiset/algorithms/graphtheory/BUILD

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,6 @@ java_binary(
181181
runtime_deps = [":graphtheory"],
182182
)
183183

184-
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:TarjanAdjacencyMatrix
185-
java_binary(
186-
name = "TarjanAdjacencyMatrix",
187-
main_class = "com.williamfiset.algorithms.graphtheory.TarjanAdjacencyMatrix",
188-
runtime_deps = [":graphtheory"],
189-
)
190184

191185
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:TarjanSccSolverAdjacencyList
192186
java_binary(

src/main/java/com/williamfiset/algorithms/graphtheory/TarjanAdjacencyMatrix.java

Lines changed: 0 additions & 115 deletions
This file was deleted.

src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java

Lines changed: 43 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* An implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
2+
* Implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
33
*
44
* <p>Verified against:
55
*
@@ -9,7 +9,9 @@
99
* <li>https://www.hackerearth.com/practice/algorithms/graphs/strongly-connected-components/tutorial
1010
* </ul>
1111
*
12-
* <p>Time complexity: O(V+E)
12+
* <p>Time: O(V + E)
13+
*
14+
* <p>Space: O(V)
1315
*
1416
* @author William Fiset, william.alexandre.fiset@gmail.com
1517
*/
@@ -21,111 +23,104 @@
2123

2224
public class TarjanSccSolverAdjacencyList {
2325

24-
private int n;
25-
private List<List<Integer>> graph;
26+
private final int n;
27+
private final List<List<Integer>> graph;
2628

2729
private boolean solved;
2830
private int sccCount, id;
29-
private boolean[] visited;
31+
private boolean[] onStack;
3032
private int[] ids, low, sccs;
3133
private Deque<Integer> stack;
3234

3335
private static final int UNVISITED = -1;
3436

37+
/**
38+
* Creates a Tarjan SCC solver for the given directed graph.
39+
*
40+
* @param graph adjacency list representation of a directed graph.
41+
* @throws IllegalArgumentException if the graph is null.
42+
*/
3543
public TarjanSccSolverAdjacencyList(List<List<Integer>> graph) {
36-
if (graph == null) throw new IllegalArgumentException("Graph cannot be null.");
44+
if (graph == null)
45+
throw new IllegalArgumentException("Graph cannot be null.");
3746
n = graph.size();
3847
this.graph = graph;
3948
}
4049

41-
// Returns the number of strongly connected components in the graph.
50+
/** Returns the number of strongly connected components in the graph. */
4251
public int sccCount() {
43-
if (!solved) solve();
52+
if (!solved)
53+
solve();
4454
return sccCount;
4555
}
4656

47-
// Get the connected components of this graph. If two indexes
48-
// have the same value then they're in the same SCC.
57+
/**
58+
* Returns the SCC id for each node. If two nodes have the same value, they belong to the same
59+
* SCC.
60+
*/
4961
public int[] getSccs() {
50-
if (!solved) solve();
62+
if (!solved)
63+
solve();
5164
return sccs;
5265
}
5366

67+
/** Runs Tarjan's algorithm. */
5468
public void solve() {
55-
if (solved) return;
69+
if (solved)
70+
return;
5671

5772
ids = new int[n];
5873
low = new int[n];
5974
sccs = new int[n];
60-
visited = new boolean[n];
75+
onStack = new boolean[n];
6176
stack = new ArrayDeque<>();
6277
Arrays.fill(ids, UNVISITED);
6378

64-
for (int i = 0; i < n; i++) {
65-
if (ids[i] == UNVISITED) {
79+
for (int i = 0; i < n; i++)
80+
if (ids[i] == UNVISITED)
6681
dfs(i);
67-
}
68-
}
6982

7083
solved = true;
7184
}
7285

7386
private void dfs(int at) {
7487
ids[at] = low[at] = id++;
7588
stack.push(at);
76-
visited[at] = true;
89+
onStack[at] = true;
7790

7891
for (int to : graph.get(at)) {
79-
if (ids[to] == UNVISITED) {
92+
if (ids[to] == UNVISITED)
8093
dfs(to);
81-
}
82-
if (visited[to]) {
94+
if (onStack[to])
8395
low[at] = min(low[at], low[to]);
84-
}
85-
/*
86-
TODO(william): investigate whether the proper way to update the lowlinks
87-
is the following bit of code. From my experience this doesn't seem to
88-
matter if the output is placed in a separate output array, but this needs
89-
further investigation.
90-
91-
if (ids[to] == UNVISITED) {
92-
dfs(to);
93-
low[at] = min(low[at], low[to]);
94-
}
95-
if (visited[to]) {
96-
low[at] = min(low[at], ids[to]);
97-
}
98-
*/
99-
10096
}
10197

102-
// On recursive callback, if we're at the root node (start of SCC)
103-
// empty the seen stack until back to root.
98+
// If we're at the root of an SCC, pop all nodes in this component off the stack.
10499
if (ids[at] == low[at]) {
105100
for (int node = stack.pop(); ; node = stack.pop()) {
106-
visited[node] = false;
101+
onStack[node] = false;
107102
sccs[node] = sccCount;
108-
if (node == at) break;
103+
if (node == at)
104+
break;
109105
}
110106
sccCount++;
111107
}
112108
}
113109

114-
// Initializes adjacency list with n nodes.
110+
/** Creates an adjacency list with n nodes. */
115111
public static List<List<Integer>> createGraph(int n) {
116112
List<List<Integer>> graph = new ArrayList<>(n);
117-
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
113+
for (int i = 0; i < n; i++)
114+
graph.add(new ArrayList<>());
118115
return graph;
119116
}
120117

121-
// Adds a directed edge from node 'from' to node 'to'
118+
/** Adds a directed edge from node 'from' to node 'to'. */
122119
public static void addEdge(List<List<Integer>> graph, int from, int to) {
123120
graph.get(from).add(to);
124121
}
125122

126-
/* Example usage: */
127-
128-
public static void main(String[] arg) {
123+
public static void main(String[] args) {
129124
int n = 8;
130125
List<List<Integer>> graph = createGraph(n);
131126

@@ -148,15 +143,9 @@ public static void main(String[] arg) {
148143
int[] sccs = solver.getSccs();
149144
Map<Integer, List<Integer>> multimap = new HashMap<>();
150145
for (int i = 0; i < n; i++) {
151-
if (!multimap.containsKey(sccs[i])) multimap.put(sccs[i], new ArrayList<>());
152-
multimap.get(sccs[i]).add(i);
146+
multimap.computeIfAbsent(sccs[i], k -> new ArrayList<>()).add(i);
153147
}
154148

155-
// Prints:
156-
// Number of Strongly Connected Components: 3
157-
// Nodes: [0, 1, 2] form a Strongly Connected Component.
158-
// Nodes: [3, 7] form a Strongly Connected Component.
159-
// Nodes: [4, 5, 6] form a Strongly Connected Component.
160149
System.out.printf("Number of Strongly Connected Components: %d\n", solver.sccCount());
161150
for (List<Integer> scc : multimap.values()) {
162151
System.out.println("Nodes: " + scc + " form a Strongly Connected Component.");

0 commit comments

Comments
 (0)