A bridge is an undirected edge whose removal increases the graph’s number of connected components.
Python graph bridges: track edge identity when parallel links are allowed
Operation contract
The network fixture records discovery times and the earliest ancestor reachable from each DFS subtree. A tree edge is a bridge only when its child subtree cannot reach the parent or an earlier ancestor through another edge. Every edge has an ID. Skipping only the parent edge, rather than every edge to the parent vertex, keeps a parallel link visible as an alternate route.
Failure and ownership boundary
Disconnected components are all visited. Self-loops and parallel edges are accepted and do not become false bridges. This fixture returns edge IDs because two physical links can have the same endpoint pair. Python union-find: connected groups with path compression and Python strongly connected components: partition a directed dependency graph solve different graph contracts.
Working program
def bridge_links(count, edges):
if type(count) is not int or not 0 <= count <= 32 or len(edges) > 256:
raise ValueError("graph budget rejected")
adjacency = [[] for _ in range(count)]
for number, edge in enumerate(edges):
if not isinstance(edge, tuple) or len(edge) != 2 or any(type(node) is not int or not 0 <= node < count for node in edge):
raise ValueError("edge rejected")
first, second = edge
adjacency[first].append((second, number)); adjacency[second].append((first, number))
discovery = [-1] * count; earliest = [0] * count; clock = 0; result = []
def visit(node, parent_edge):
nonlocal clock
discovery[node] = earliest[node] = clock; clock += 1
for neighbor, edge_id in adjacency[node]:
if edge_id == parent_edge: continue
if discovery[neighbor] < 0:
visit(neighbor, edge_id)
earliest[node] = min(earliest[node], earliest[neighbor])
if earliest[neighbor] > discovery[node]: result.append(edge_id)
else:
earliest[node] = min(earliest[node], discovery[neighbor])
for node in range(count):
if discovery[node] < 0: visit(node, -1)
return sorted(result)
print("parallel-safe bridges:", bridge_links(4, [(0,1),(0,1),(1,2),(2,3),(3,2)]))
print("cycle bridges:", bridge_links(3, [(0,1),(1,2),(2,0)]))Output
parallel-safe bridges: [2]
cycle bridges: []Costs and limits
DFS uses O(V+E) traversal work and retained adjacency, plus sorting of returned edge IDs. The vertex and edge budgets bound recursion and retained input here. This is a topology check, not a physical reliability estimate.
Common Mistakes
- Skip the incoming edge ID, not every edge with the parent endpoint.
- A self-loop cannot connect two otherwise separate components.
Connected lessons
Python union-find: connected groups with path compression, Python strongly connected components: partition a directed dependency graph, Python graph BFS: mark a vertex when it enters the queue.
Follow the service contract
Python articulation points: find vertices that separate an undirected graph, Python directed Euler trail: consume every edge identity exactly once.
