Critical Connections in a Network
There are n servers numbered 0 to n - 1, joined by two-way connections. A connection is critical if removing it would leave some server unable to reach some other server. Return every critical connection, in any order. Such an edge is also called a bridge . Walk the network depth first and stamp every server with the time you first arrived . Then work out, for each server, the earliest arrival time anything in its part of the walk can reach by going backwards. If the best a neighbour can do is later than your own arrival time, nothing down there loops back past you, so the edge between you is a bridge.
Constraints
- 2 ≤ n ≤ 105
- n - 1 ≤ connections.length ≤ 105
- No connection is repeated and no server is joined to itself
- The network starts out connected
Example
n = 4, connections = [[0, 1], [1, 2], [2, 0], [1, 3]][[1, 3]]Explanation Servers 0, 1 and 2 sit in a triangle, so any one of those edges can be cut and they still reach each other. Server 3 hangs off server 1 by a single edge, so that edge is critical.
In plain terms
- Bridge
- An edge whose removal splits the graph into more pieces than before. Every critical connection is a bridge.
- Discovery time
- A counter stamped on each node the first time the depth-first walk arrives there. Earlier numbers mean the node was reached sooner.
- Low-link value
- The smallest discovery time reachable from a node, using its own subtree plus at most one edge back up. If a neighbour cannot reach past you, the edge to it is a bridge.
- Back edge
- An edge that leads to a node already visited. A back edge is the only way a deeper node can reach an earlier discovery time.
One cell per server, value = discovery time / low-link value
Four servers, nothing visited, and the clock starts at zero.
What happens in this step
connections = [[0, 1], [1, 2], [2, 0], [1, 3]] discovery and low = unset for all four servers timer = 0 bridges = [] Servers 0, 1 and 2 form a triangle; server 3 hangs off server 1.
Steps to visualize
- The row has one cell per server. The value is written as discovery time, then a slash, then the low-link value.
- A dash means the walk has not reached that server yet.
- Arriving at a server stamps both numbers with the current clock reading.
- Meeting an already visited server pulls the low-link value down towards that server’s earlier discovery time.
- Coming back up an edge, if the child’s low-link is still larger than the parent’s discovery time, that edge is a bridge.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
Four servers, nothing visited, and the clock starts at zero.
What happens in this step
connections = [[0, 1], [1, 2], [2, 0], [1, 3]] discovery and low = unset for all four servers timer = 0 bridges = [] Servers 0, 1 and 2 form a triangle; server 3 hangs off server 1.
Solution
function criticalConnections(n, connections) {
const graph = Array.from({ length: n }, () => []);
for (const edge of connections) {
graph[edge[0]].push(edge[1]);
graph[edge[1]].push(edge[0]);
}
const discovery = new Array(n).fill(-1);
const low = new Array(n).fill(-1);
const bridges = [];
let timer = 0;
function dfs(node, parent) {
discovery[node] = timer;
low[node] = timer;
timer += 1;
for (const next of graph[node]) {
if (next === parent) continue;
if (discovery[next] === -1) {
dfs(next, node);
low[node] = Math.min(low[node], low[next]);
if (low[next] > discovery[node]) {
bridges.push([node, next]);
}
} else {
low[node] = Math.min(low[node], discovery[next]);
}
}
}
for (let node = 0; node < n; node++) {
if (discovery[node] === -1) {
dfs(node, -1);
}
}
return bridges;
}- Time
- O(n + e), where e is the number of connections
- Space
- O(n + e)
Test cases
| Input | Expected | Covers |
|---|---|---|
n = 4, connections = [[0, 1], [1, 2], [2, 0], [1, 3]] | [[1, 3]] | example from the docstring, a triangle with one tail |
n = 2, connections = [[0, 1]] | [[0, 1]] | smallest network, where the only edge is critical |
n = 4, connections = [[0, 1], [1, 2], [2, 3]] | [[0, 1], [1, 2], [2, 3]] | a chain, where every single edge is critical |
n = 3, connections = [[0, 1], [1, 2], [2, 0]] | [] | a ring with no critical edge at all |
n = 6, connections = [[0, 1], [1, 2], [2, 0], [1, 3], [3, 4], [4, 5], [5, 3]] | [[1, 3]] | two rings joined by a single edge |
n = 5, connections = [[0, 1], [1, 2], [3, 4]] | [[0, 1], [1, 2], [3, 4]] | a network already in two pieces, so the outer loop restarts |