Undirected graph
In the following undirected graph
- An edge
of an undirected graph is a bridge if and only if lies on no cycle of . - Every edge of an undirected tree is a bridge.
Let
- Let
and be two vertices of the DFS tree such that is an ancestor of . Also, and are not adjacent.- If there’s a back edge
, then none of the edges in the path are bridges. If we remove one of them, the graph is still connected because of this edge. - Otherwise, the edge is a bridge.
- If there’s a back edge
Implementation notes
- To check if a successor of a vertex
has a back edge to a predecessor of , an additional state is stored in each vertex, which is the discovery time of the lowest back edge of a successor of (by lowest back edge, we mean the back edge to a vertex with the lowest discovery time), denoted as . Initially, this state is set to the discovery time of the vertex , i.e., . This state is propagated when backtracking is performed. - Let
be a back edge. When this edge is analyzed, the state needs to be updated to be the minimum of the existing and the discovery time of , i.e., . - Let
be an adjacent successor of in the DFS tree. When we’ve finished analyzing the branch of the tree because of the edge, we have to check if the state contains a back edge to some predecessor of ( is propagated), i.e., . If so, then is not a bridge.
int time_spent;
// the adjacency list representation of `G`
vector<vector<int> > g;
// the time a vertex `i` was discovered first
vector<int> time_in;
// stores the discovery time of the lowest predecessor that vertex `i`'s
// succesor vertices can reach **through a back edge**, initially
// the lowest predecessor is set to the vertex itself
vector<int> back;
// the bridges found during the dfs
vector<pair<int, int> > cut_edge;
void dfs(int v, int parent) {
// the lowest back edge discovery time of `v` is
// set to the discovery time of `v` initally
back[v] = time_in[v] = ++time_spent;
for (int i = 0; i < g[v].size(); i += 1) {
int next = g[v][i];
if (next == parent) {
continue;
}
if (time_in[next] == -1) {
dfs(next, v);
// if there's a back edge between a descendant of `next` and
// a predecessor of `v` then `next` will have a lower back edge discovery time
// otherwise it's a bridge
if (back[next] > time_in[v]) {
cut_edge.push_back(pair<int, int> (v, next));
}
// propagation of the lowest back edge discovery time
back[v] = min(back[v], back[next]);
} else {
// *back edge*
// update the lowest back edge discovery time of `v`
back[v] = min(back[v], time_in[next]);
}
}
}
/**
* Finds the bridges in an undirected graph `G` of order `n` and size `m`
*
* Time complexity: O(n + m)
* Space complexity: O(n)
*/
void bridges() {
int n = g.size();
time_spent = 0;
time_in.assign(n, -1);
back.assign(n, -1);
cut_edge.clear();
for (int i = 0; i < n; i += 1) {
if (time_in[i] == -1) {
dfs(i, -1);
}
}
}
Directed graph (strong bridges)
Let
The following is a connected graph
A trivial algorithm to find the strong bridges of a digraph
- Compute the number of strongly connected components of
, denoted as . - For each edge
: - remove
from - compute the number of strongly connected components of
, denoted as - if
, then is a bridge.
The time complexity of the algorithm above is clearly
Let
http://www.sofsem.cz/sofsem12/files/presentations/Thursday/GiuseppeItaliano.pdf