Skip to content

Commit e8511ed

Browse files
williamfisetclaude
andauthored
Refactor ArticulationPointsAdjacencyList: add docs, comments, and graph diagrams (williamfiset#1286)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3ae99f7 commit e8511ed

1 file changed

Lines changed: 55 additions & 18 deletions

File tree

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

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
/**
2-
* Finds all articulation points on an undirected graph.
2+
* Articulation Points (Cut Vertices) — Adjacency List
33
*
4-
* <p>Tested against HackerEarth online judge at:
4+
* An articulation point is a vertex whose removal disconnects the graph
5+
* (or increases the number of connected components). This implementation
6+
* uses Tarjan's DFS-based algorithm with low-link values.
7+
*
8+
* For each DFS tree rooted at node r, a non-root node u is an articulation
9+
* point if it has a child v such that no vertex in the subtree rooted at v
10+
* has a back edge to an ancestor of u:
11+
*
12+
* ids[u] <= low[v]
13+
*
14+
* The root node r is an articulation point if it has more than one child
15+
* in the DFS tree.
16+
*
17+
* Works on disconnected graphs by running DFS from every unvisited node.
18+
*
19+
* See also: {@link BridgesAdjacencyList} for finding bridge edges.
20+
*
21+
* Tested against HackerEarth online judge at:
522
* https://www.hackerearth.com/practice/algorithms/graphs/articulation-points-and-bridges/tutorial
623
*
24+
* Time: O(V + E)
25+
* Space: O(V)
26+
*
727
* @author William Fiset, william.alexandre.fiset@gmail.com
828
*/
929
package com.williamfiset.algorithms.graphtheory;
@@ -15,33 +35,38 @@
1535

1636
public class ArticulationPointsAdjacencyList {
1737

18-
private int n, id, rootNodeOutgoingEdgeCount;
38+
private final int n;
39+
private final List<List<Integer>> graph;
1940
private boolean solved;
41+
private int id, rootNodeOutgoingEdgeCount;
2042
private int[] low, ids;
2143
private boolean[] visited, isArticulationPoint;
22-
private List<List<Integer>> graph;
2344

2445
public ArticulationPointsAdjacencyList(List<List<Integer>> graph, int n) {
2546
if (graph == null || n <= 0 || graph.size() != n) throw new IllegalArgumentException();
2647
this.graph = graph;
2748
this.n = n;
2849
}
2950

30-
// Returns the indexes for all articulation points in the graph even if the
31-
// graph is not fully connected.
51+
/**
52+
* Returns a boolean array where index i is true if node i is an articulation point.
53+
* Works even if the graph is not fully connected.
54+
*/
3255
public boolean[] findArticulationPoints() {
3356
if (solved) return isArticulationPoint;
3457

3558
id = 0;
36-
low = new int[n]; // Low link values
37-
ids = new int[n]; // Nodes ids
59+
low = new int[n];
60+
ids = new int[n];
3861
visited = new boolean[n];
3962
isArticulationPoint = new boolean[n];
4063

64+
// Run DFS from each unvisited node to handle disconnected components.
4165
for (int i = 0; i < n; i++) {
4266
if (!visited[i]) {
4367
rootNodeOutgoingEdgeCount = 0;
4468
dfs(i, i, -1);
69+
// Root is an articulation point only if it has 2+ children in the DFS tree.
4570
isArticulationPoint[i] = (rootNodeOutgoingEdgeCount > 1);
4671
}
4772
}
@@ -51,48 +76,59 @@ public boolean[] findArticulationPoints() {
5176
}
5277

5378
private void dfs(int root, int at, int parent) {
54-
5579
if (parent == root) rootNodeOutgoingEdgeCount++;
5680

5781
visited[at] = true;
5882
low[at] = ids[at] = id++;
5983

60-
List<Integer> edges = graph.get(at);
61-
for (Integer to : edges) {
84+
for (int to : graph.get(at)) {
6285
if (to == parent) continue;
6386
if (!visited[to]) {
6487
dfs(root, to, at);
6588
low[at] = min(low[at], low[to]);
89+
// If no vertex in the subtree rooted at 'to' can reach above 'at',
90+
// then removing 'at' would disconnect 'to's subtree.
6691
if (ids[at] <= low[to]) {
6792
isArticulationPoint[at] = true;
6893
}
6994
} else {
95+
// Back edge: update low-link to the earliest reachable ancestor.
7096
low[at] = min(low[at], ids[to]);
7197
}
7298
}
7399
}
74100

75101
/* Graph helpers */
76102

77-
// Initialize a graph with 'n' nodes.
78103
public static List<List<Integer>> createGraph(int n) {
79104
List<List<Integer>> graph = new ArrayList<>(n);
80105
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
81106
return graph;
82107
}
83108

84-
// Add an undirected edge to a graph.
85109
public static void addEdge(List<List<Integer>> graph, int from, int to) {
86110
graph.get(from).add(to);
87111
graph.get(to).add(from);
88112
}
89113

90-
/* Example usage: */
114+
// ==================== Main ====================
91115

92116
public static void main(String[] args) {
117+
testExample1();
93118
testExample2();
94119
}
95120

121+
//
122+
// 0 --- 1
123+
// | /
124+
// 2 -------- 3 --- 4
125+
// |
126+
// 5 --- 6
127+
// | |
128+
// 8 --- 7
129+
//
130+
// Articulation points: 2, 3, 5
131+
//
96132
private static void testExample1() {
97133
int n = 9;
98134
List<List<Integer>> graph = createGraph(n);
@@ -111,16 +147,18 @@ private static void testExample1() {
111147
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
112148
boolean[] isArticulationPoint = solver.findArticulationPoints();
113149

114-
// Prints:
115150
// Node 2 is an articulation
116151
// Node 3 is an articulation
117152
// Node 5 is an articulation
118153
for (int i = 0; i < n; i++)
119154
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
120155
}
121156

122-
// Tests a graph with 3 nodes in a line: A - B - C
123-
// Only node 'B' should be an articulation point.
157+
//
158+
// 0 --- 1 --- 2
159+
//
160+
// Articulation point: 1
161+
//
124162
private static void testExample2() {
125163
int n = 3;
126164
List<List<Integer>> graph = createGraph(n);
@@ -131,7 +169,6 @@ private static void testExample2() {
131169
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
132170
boolean[] isArticulationPoint = solver.findArticulationPoints();
133171

134-
// Prints:
135172
// Node 1 is an articulation
136173
for (int i = 0; i < n; i++)
137174
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);

0 commit comments

Comments
 (0)