Repository navigation
Create Bridges.java #7659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Create Bridges.java #7659
Changes from all commits
711b1fe
d3886e1
b5149b0
9f54bcc
7918aad
91b3d76
6a21425
c5b315f
28877ae
06e717e
3fb9a74
423cc3a
a6f7c26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| package com.thealgorithms.graph; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Finds bridges in an undirected graph using depth-first search. | ||
| * | ||
| * <p>A bridge is an edge whose removal increases the number of connected | ||
| * components in the graph. | ||
| * | ||
| * <p>Vertices are numbered from 0 to {@code vertexCount - 1}. | ||
| * | ||
| * <p>Time complexity: O(V + E), where V is the number of vertices | ||
| * and E is the number of edges. | ||
| * | ||
| * <p>Reference: https://en.wikipedia.org/wiki/Bridge_(graph_theory) | ||
| */ | ||
| public final class Bridges { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This package already contains Please consolidate this contribution into that implementation rather than maintaining a second copy of the algorithm. In particular, preserve the edge-ID handling and add the parallel-edge regression test to |
||
|
|
||
| private Bridges() { | ||
| // Utility class; do not instantiate. | ||
| } | ||
|
|
||
| /** | ||
| * Returns all bridges in an undirected graph. | ||
| * | ||
| * @param vertexCount number of vertices | ||
| * @param edges array of edges, with each edge represented as {u, v} | ||
| * @return list of bridges, with each bridge represented as {u, v} | ||
| * @throws IllegalArgumentException if the vertex count is negative, | ||
| * or an edge is null, malformed, | ||
| * or references an invalid vertex | ||
| */ | ||
| public static List<int[]> findBridges(int vertexCount, int[][] edges) { | ||
| if (vertexCount < 0) { | ||
| throw new IllegalArgumentException("Vertex count cannot be negative."); | ||
| } | ||
|
|
||
| if (edges == null) { | ||
| throw new IllegalArgumentException("Edges cannot be null."); | ||
| } | ||
|
|
||
| List<List<int[]>> adjacencyList = new ArrayList<>(vertexCount); | ||
| for (int i = 0; i < vertexCount; i++) { | ||
| adjacencyList.add(new ArrayList<>()); | ||
| } | ||
|
|
||
| for (int edgeId = 0; edgeId < edges.length; edgeId++) { | ||
| int[] edge = edges[edgeId]; | ||
| if (edge == null || edge.length != 2) { | ||
| throw new IllegalArgumentException("Each edge must contain exactly two vertices."); | ||
| } | ||
|
|
||
| int u = edge[0]; | ||
| int v = edge[1]; | ||
| if (u < 0 || u >= vertexCount || v < 0 || v >= vertexCount) { | ||
| throw new IllegalArgumentException("Edge contains an invalid vertex."); | ||
| } | ||
|
|
||
| adjacencyList.get(u).add(new int[] {v, edgeId}); | ||
| adjacencyList.get(v).add(new int[] {u, edgeId}); | ||
| } | ||
|
|
||
| int[] discoveryTime = new int[vertexCount]; | ||
| int[] low = new int[vertexCount]; | ||
| Arrays.fill(discoveryTime, -1); | ||
|
|
||
| List<int[]> bridges = new ArrayList<>(); | ||
| int[] timer = {0}; | ||
|
|
||
| for (int vertex = 0; vertex < vertexCount; vertex++) { | ||
| if (discoveryTime[vertex] == -1) { | ||
| dfs(vertex, -1, adjacencyList, discoveryTime, low, timer, bridges); | ||
| } | ||
| } | ||
|
|
||
| bridges.sort((first, second) -> { | ||
| int comparison = Integer.compare(first[0], second[0]); | ||
| return comparison != 0 ? comparison : Integer.compare(first[1], second[1]); | ||
| }); | ||
|
|
||
| return bridges; | ||
| } | ||
|
|
||
| private static void dfs(int vertex, int parentEdgeId, List<List<int[]>> adjacencyList, int[] discoveryTime, int[] low, int[] timer, List<int[]> bridges) { | ||
| discoveryTime[vertex] = timer[0]; | ||
| low[vertex] = timer[0]++; | ||
|
|
||
| for (int[] adjacent : adjacencyList.get(vertex)) { | ||
| int neighbor = adjacent[0]; | ||
| int edgeId = adjacent[1]; | ||
|
|
||
| if (edgeId == parentEdgeId) { | ||
| continue; | ||
| } | ||
|
|
||
| if (discoveryTime[neighbor] != -1) { | ||
| low[vertex] = Math.min(low[vertex], discoveryTime[neighbor]); | ||
| } else { | ||
| dfs(neighbor, edgeId, adjacencyList, discoveryTime, low, timer, bridges); | ||
|
|
||
| low[vertex] = Math.min(low[vertex], low[neighbor]); | ||
|
|
||
| if (low[neighbor] > discoveryTime[vertex]) { | ||
| bridges.add(new int[] {Math.min(vertex, neighbor), Math.max(vertex, neighbor)}); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| package com.thealgorithms.graph; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
|
||
| import java.util.List; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class BridgesTest { | ||
|
|
||
| @Test | ||
| void testTriangleHasNoBridges() { | ||
| int[][] edges = {{0, 1}, {1, 2}, {2, 0}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(3, edges); | ||
|
|
||
| assertEquals(0, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| void testChainHasTwoBridges() { | ||
| int[][] edges = {{0, 1}, {1, 2}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(3, edges); | ||
|
|
||
| assertEquals(2, result.size()); | ||
| assertArrayEquals(new int[] {0, 1}, result.get(0)); | ||
| assertArrayEquals(new int[] {1, 2}, result.get(1)); | ||
| } | ||
|
|
||
| @Test | ||
| void testDisconnectedGraph() { | ||
| int[][] edges = {{0, 1}, {2, 3}, {3, 4}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(5, edges); | ||
|
|
||
| assertEquals(3, result.size()); | ||
| assertArrayEquals(new int[] {0, 1}, result.get(0)); | ||
| assertArrayEquals(new int[] {2, 3}, result.get(1)); | ||
| assertArrayEquals(new int[] {3, 4}, result.get(2)); | ||
| } | ||
|
|
||
| @Test | ||
| void testEmptyGraph() { | ||
| List<int[]> result = Bridges.findBridges(4, new int[0][]); | ||
|
|
||
| assertEquals(0, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| void testZeroVertices() { | ||
| List<int[]> result = Bridges.findBridges(0, new int[0][]); | ||
|
|
||
| assertEquals(0, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| void testParallelEdgesAreNotBridges() { | ||
| int[][] edges = {{0, 1}, {0, 1}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(2, edges); | ||
|
|
||
| assertEquals(0, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| void testSelfLoopIsNotBridge() { | ||
| int[][] edges = {{0, 0}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(1, edges); | ||
|
|
||
| assertEquals(0, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| void testSingleEdgeIsBridge() { | ||
| int[][] edges = {{0, 1}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(2, edges); | ||
|
|
||
| assertEquals(1, result.size()); | ||
| assertArrayEquals(new int[] {0, 1}, result.get(0)); | ||
| } | ||
|
|
||
| @Test | ||
| void testCycleWithTail() { | ||
| int[][] edges = {{0, 1}, {1, 2}, {2, 0}, {2, 3}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(4, edges); | ||
|
|
||
| assertEquals(1, result.size()); | ||
| assertArrayEquals(new int[] {2, 3}, result.get(0)); | ||
| } | ||
|
|
||
| @Test | ||
| void testNegativeVertexCountThrowsException() { | ||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(-1, new int[0][])); | ||
| } | ||
|
|
||
| @Test | ||
| void testNullEdgesThrowsException() { | ||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, null)); | ||
| } | ||
|
|
||
| @Test | ||
| void testMalformedEdgeThrowsException() { | ||
| int[][] edges = {{0}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testEdgeWithTooManyVerticesThrowsException() { | ||
| int[][] edges = {{0, 1, 2}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(3, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testNullEdgeThrowsException() { | ||
| int[][] edges = {null}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testNegativeFirstVertexThrowsException() { | ||
| int[][] edges = {{-1, 1}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testNegativeSecondVertexThrowsException() { | ||
| int[][] edges = {{0, -1}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testFirstVertexOutOfRangeThrowsException() { | ||
| int[][] edges = {{2, 1}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testSecondVertexOutOfRangeThrowsException() { | ||
| int[][] edges = {{0, 2}}; | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> Bridges.findBridges(2, edges)); | ||
| } | ||
|
|
||
| @Test | ||
| void testBridgesAreSortedByBothEndpoints() { | ||
| int[][] edges = {{2, 3}, {0, 2}, {0, 1}}; | ||
|
|
||
| List<int[]> result = Bridges.findBridges(4, edges); | ||
|
|
||
| assertEquals(3, result.size()); | ||
| assertArrayEquals(new int[] {0, 1}, result.get(0)); | ||
| assertArrayEquals(new int[] {0, 2}, result.get(1)); | ||
| assertArrayEquals(new int[] {2, 3}, result.get(2)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
O(V + E)claim does not account forbridges.sort(...)at lines 79–82. WithBreturned bridges, the implemented method has an upper bound ofO(V + E + B log B). Please update the complexity documentation if sorted output is retained, and document the endpoint normalization and lexicographic ordering in the return contract. Alternatively, remove the sorting if ordering is not part of the intended API.