11/**
2- * Finds all the bridges on an undirected graph.
2+ * Bridge Edges (Cut Edges) — Adjacency List
33 *
4- * <p>Test against HackerEarth online judge at:
4+ * <p>A bridge is an edge whose removal disconnects the graph (or increases
5+ * the number of connected components). This implementation uses Tarjan's
6+ * DFS-based algorithm with low-link values.
7+ *
8+ * <p>An edge (u, v) is a bridge if no vertex in the subtree rooted at v
9+ * (in the DFS tree) has a back edge to u or any ancestor of u:
10+ *
11+ * <pre> ids[u] < low[v]</pre>
12+ *
13+ * <p><strong>Limitation:</strong> This implementation assumes a simple graph (no
14+ * parallel/multi-edges between the same pair of nodes). If parallel edges exist,
15+ * the algorithm may incorrectly report an edge as a bridge even though a second
16+ * edge still connects the two nodes. This is because the parent-skip logic
17+ * ({@code to == parent}) skips <em>all</em> occurrences of the parent in the
18+ * adjacency list, rather than only the single tree edge used to arrive at the
19+ * current node.
20+ *
21+ * <p>Works on disconnected graphs by running DFS from every unvisited node.
22+ *
23+ * <p>See also: {@link ArticulationPointsAdjacencyList} for finding cut vertices.
24+ *
25+ * <p>Tested against HackerEarth online judge at:
526 * https://www.hackerearth.com/practice/algorithms/graphs/articulation-points-and-bridges/tutorial
627 *
28+ * <p>Time: O(V + E)
29+ * <p>Space: O(V)
30+ *
731 * @author William Fiset, william.alexandre.fiset@gmail.com
832 */
933package com .williamfiset .algorithms .graphtheory ;
1539
1640public class BridgesAdjacencyList {
1741
18- private int n , id ;
19- private int [] low , ids ;
42+ private final int n ;
43+ private final List < List < Integer >> graph ;
2044 private boolean solved ;
45+ private int id ;
46+ private int [] low , ids ;
2147 private boolean [] visited ;
22- private List <List <Integer >> graph ;
23- private List <Integer > bridges ;
48+ private List <int []> bridges ;
2449
2550 public BridgesAdjacencyList (List <List <Integer >> graph , int n ) {
26- if (graph == null || n <= 0 || graph .size () != n ) throw new IllegalArgumentException ();
51+ if (graph == null || n <= 0 || graph .size () != n ) {
52+ throw new IllegalArgumentException ();
53+ }
2754 this .graph = graph ;
2855 this .n = n ;
2956 }
3057
31- // Returns a list of pairs of nodes indicating which nodes form bridges.
32- // The returned list is always of even length and indexes (2*i, 2*i+1) form a
33- // pair. For example, nodes at indexes (0, 1) are a pair, (2, 3) are another
34- // pair, etc...
35- public List <Integer > findBridges () {
36- if (solved ) return bridges ;
58+ /**
59+ * Returns a list of bridge edges. Each element is an {@code int[]} of length 2
60+ * where {@code [0]} and {@code [1]} are the node indices on either side of the bridge.
61+ * For example, if node 2 and node 5 are connected by a bridge, the entry is {@code {2, 5}}.
62+ */
63+ public List <int []> findBridges () {
64+ if (solved ) {
65+ return bridges ;
66+ }
3767
3868 id = 0 ;
39- low = new int [n ]; // Low link values
40- ids = new int [n ]; // Nodes ids
69+ low = new int [n ];
70+ ids = new int [n ];
4171 visited = new boolean [n ];
42-
4372 bridges = new ArrayList <>();
4473
45- // Finds all bridges in the graph across various connected components.
46- for (int i = 0 ; i < n ; i ++) if (!visited [i ]) dfs (i , -1 , bridges );
74+ // Run DFS from each unvisited node to handle disconnected components.
75+ for (int i = 0 ; i < n ; i ++) {
76+ if (!visited [i ]) {
77+ dfs (i , -1 );
78+ }
79+ }
4780
4881 solved = true ;
4982 return bridges ;
5083 }
5184
52- private void dfs (int at , int parent , List <Integer > bridges ) {
53-
85+ private void dfs (int at , int parent ) {
5486 visited [at ] = true ;
5587 low [at ] = ids [at ] = ++id ;
5688
57- for (Integer to : graph .get (at )) {
58- if (to == parent ) continue ;
89+ for (int to : graph .get (at )) {
90+ if (to == parent ) {
91+ continue ;
92+ }
5993 if (!visited [to ]) {
60- dfs (to , at , bridges );
94+ dfs (to , at );
6195 low [at ] = min (low [at ], low [to ]);
96+ // If no vertex in the subtree rooted at 'to' can reach 'at' or above,
97+ // then removing edge (at, to) would disconnect the graph.
6298 if (ids [at ] < low [to ]) {
63- bridges .add (at );
64- bridges .add (to );
99+ bridges .add (new int [] {at , to });
65100 }
66101 } else {
102+ // Back edge: update low-link to the earliest reachable ancestor.
67103 low [at ] = min (low [at ], ids [to ]);
68104 }
69105 }
70106 }
71107
72- /* Example usage: */
108+ /* Graph helpers */
73109
74- public static void main (String [] args ) {
110+ public static List <List <Integer >> createGraph (int n ) {
111+ List <List <Integer >> graph = new ArrayList <>(n );
112+ for (int i = 0 ; i < n ; i ++) {
113+ graph .add (new ArrayList <>());
114+ }
115+ return graph ;
116+ }
75117
118+ public static void addEdge (List <List <Integer >> graph , int from , int to ) {
119+ graph .get (from ).add (to );
120+ graph .get (to ).add (from );
121+ }
122+
123+ // ==================== Main ====================
124+
125+ //
126+ // 0 --- 1
127+ // | /
128+ // 2 -------- 5 --- 6
129+ // | | |
130+ // 3 --- 4 8 --- 7
131+ //
132+ // Bridges: (2,3), (3,4), (2,5)
133+ //
134+ public static void main (String [] args ) {
76135 int n = 9 ;
77136 List <List <Integer >> graph = createGraph (n );
78137
@@ -88,29 +147,10 @@ public static void main(String[] args) {
88147 addEdge (graph , 8 , 5 );
89148
90149 BridgesAdjacencyList solver = new BridgesAdjacencyList (graph , n );
91- List <Integer > bridges = solver .findBridges ();
92-
93- // Prints:
94- // Bridge between nodes: 3 and 4
95- // Bridge between nodes: 2 and 3
96- // Bridge between nodes: 2 and 5
97- for (int i = 0 ; i < bridges .size () / 2 ; i ++) {
98- int node1 = bridges .get (2 * i );
99- int node2 = bridges .get (2 * i + 1 );
100- System .out .printf ("Bridge between nodes: %d and %d\n " , node1 , node2 );
101- }
102- }
103-
104- // Initialize graph with 'n' nodes.
105- public static List <List <Integer >> createGraph (int n ) {
106- List <List <Integer >> graph = new ArrayList <>();
107- for (int i = 0 ; i < n ; i ++) graph .add (new ArrayList <>());
108- return graph ;
109- }
150+ List <int []> bridges = solver .findBridges ();
110151
111- // Add undirected edge to graph.
112- public static void addEdge (List <List <Integer >> graph , int from , int to ) {
113- graph .get (from ).add (to );
114- graph .get (to ).add (from );
152+ for (int [] bridge : bridges ) {
153+ System .out .printf ("Bridge between nodes: %d and %d\n " , bridge [0 ], bridge [1 ]);
154+ }
115155 }
116156}
0 commit comments