Undirected graph

In the following undirected graph G, the edges v2v3 and v3v4 are bridges.

0123456
  • An edge e of an undirected graph G is a bridge if and only if e lies on no cycle of G.
  • Every edge of an undirected tree is a bridge.

Let G be an undirected graph. By analyzing the properties of the DFS tree, we can determine if an edge is a bridge given the following facts:

  • Let u and v be two vertices of the DFS tree such that u is an ancestor of v. Also, u and v are not adjacent.
    • If there’s a back edge vu, then none of the edges in the uv path are bridges. If we remove one of them, the graph is still connected because of this edge.
    • Otherwise, the edge is a bridge.

Implementation notes

  • To check if a successor of a vertex u has a back edge to a predecessor of u, an additional state is stored in each vertex, which is the discovery time of the lowest back edge of a successor of u (by lowest back edge, we mean the back edge to a vertex with the lowest discovery time), denoted as uback. Initially, this state is set to the discovery time of the vertex v, i.e., uback=uin. This state is propagated when backtracking is performed.
  • Let uv be a back edge. When this edge is analyzed, the vback state needs to be updated to be the minimum of the existing vback and the discovery time of u, i.e., vback=min(vback,uin).
  • Let v be an adjacent successor of u in the DFS tree. When we’ve finished analyzing the branch of the tree because of the uv edge, we have to check if the vback state contains a back edge to some predecessor of u (vback is propagated), i.e., uin>vback. If so, then uv 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 G be a directed graph. An edge uvE(G) is a strong bridge if its removal increases the number of strongly connected components of G.

The following is a connected graph G. Every edge but v2v0 is a strong bridge because removing it from G increases the number of strongly connected components. Removing v2v0 doesn’t increase the number of strongly connected components, so it’s not a bridge.

0123

A trivial algorithm to find the strong bridges of a digraph G of order n and size m is as follows:

  • Compute the number of strongly connected components of G, denoted as k(G).
  • For each edge eE(G):
  • remove e from G
  • compute the number of strongly connected components of G, denoted as k(Ge)
  • if k(G)<k(Ge), then e is a bridge.

The time complexity of the algorithm above is clearly O(m(n+m)).

Let uv be an edge of a digraph G. We say that uv is redundant if there’s an alternative path from vertex u to vertex v avoiding uv. Otherwise, we say that uv is not redundant. Computing the strong bridges is equivalent to computing the non-redundant edges of a graph.

http://www.sofsem.cz/sofsem12/files/presentations/Thursday/GiuseppeItaliano.pdf