← All Posts
DSA Series · Graphs · Bridges & Articulation Points

Applications of Bridges & Articulation Points

The previous posts built up the theory and algorithms for finding bridges and articulation points. This post is about putting that theory to work. We cover the most important real-world applications, the data structures built on top of bridge and AP detection, and a curated set of practice problems to solidify your understanding.

Network Reliability Analysis

The original motivation for studying bridges and articulation points comes from network engineering. A bridge in a network is a link whose failure disconnects the network. An articulation point is a node whose failure disconnects the network. Both represent single points of failure.

Consider a corporate network connecting six offices. Traffic flows along the edges. If any single link or router goes down, does the entire network stay connected?

BRIDGE A B C D E F G H Articulation point (single point of failure) Bridge (critical link)

A network with two clusters. Edge D‑E is a bridge. Nodes D and E are articulation points. Losing either disconnects the network.

Running Tarjan's algorithm on this network immediately identifies D and E as articulation points and the edge D‑E as a bridge. The engineering response is straightforward: add a redundant link between the two clusters (for example, C‑G) so that no single failure can partition the network.

Practical note: Internet backbone engineers use bridge and AP detection as part of routine network audits. Any time a new topology is deployed, automated tools verify that the graph is 2-edge-connected (no bridges) and 2-vertex-connected (no APs). If not, redundant links are added until both conditions hold.

Other real-world domains

Biconnected Components

A graph is biconnected if it is connected and has no articulation points. Equivalently, between every pair of vertices there exist at least two vertex-disjoint paths. A biconnected component (also called a 2-connected component or block) is a maximal biconnected subgraph.

The key insight: articulation points are shared between biconnected components. Every non-AP vertex belongs to exactly one biconnected component. Every AP belongs to two or more.

How bridges and APs partition the graph

The biconnected component decomposition works as follows:

  1. Run DFS and find all articulation points.
  2. Maintain a stack of edges during the DFS.
  3. When you identify a biconnected component (the subtree rooted at a child of an AP has low[child] >= disc[node]), pop all edges from the stack until you reach the current edge. Those edges form one biconnected component.
  4. A bridge is a biconnected component that contains exactly one edge.
Block 1 Block 2 Block 3 Block 4 1 2 3 4 5 6 7 8 9 10 11 Articulation point (shared between blocks) Bridge (single-edge block)

Four biconnected components (blocks). Nodes 3, 6, and 7 are articulation points shared between blocks. The edge 6‑7 is a bridge and forms its own block.

The algorithm for finding biconnected components runs in O(V + E) time, the same as a single DFS traversal. You simply augment the articulation point algorithm with an edge stack.

void dfs(int u, int parent, int& timer,
         vector<vector<int>>& adj,
         vector<int>& disc, vector<int>& low,
         stack<pair<int,int>>& st,
         vector<vector<pair<int,int>>>& components) {
    disc[u] = low[u] = timer++;
    int children = 0;
    for (int v : adj[u]) {
        if (disc[v] == -1) {
            children++;
            st.push({u, v});
            dfs(v, u, timer, adj, disc, low, st, components);
            low[u] = min(low[u], low[v]);
            // Check if u is an AP or root with multiple children
            bool isAP = (parent == -1 && children > 1)
                      || (parent != -1 && low[v] >= disc[u]);
            if (isAP) {
                // Pop edges until (u, v) to form a component
                vector<pair<int,int>> comp;
                while (st.top() != make_pair(u, v)) {
                    comp.push_back(st.top());
                    st.pop();
                }
                comp.push_back(st.top());
                st.pop();
                components.push_back(comp);
            }
        } else if (v != parent && disc[v] < disc[u]) {
            st.push({u, v});
            low[u] = min(low[u], disc[v]);
        }
    }
}

Block-Cut Tree

Once you have the biconnected component decomposition, the next natural step is to build the block-cut tree. This is a tree where:

The result is always a tree (or a forest if the original graph is disconnected). The block-cut tree compresses the structure of the graph: all cycles within a block are collapsed into a single node, and the tree captures how the blocks are glued together via cut vertices.

Why is this useful?

Many graph problems become tree problems on the block-cut tree. Since tree problems are generally much easier (LCA, path queries, subtree queries), this transformation is extremely powerful. For example:

Original Graph 1 2 3 4 5 6 decompose Block-Cut Tree B1 {1,2,3} B2 {3,4} B3 {4,5,6} 3 Block (biconnected component) Cut vertex

A graph with 6 vertices decomposes into three blocks (B1, B2, B3) connected through cut vertices 3 and 4, forming a tree.

// Building the block-cut tree after finding biconnected components
// blocks[i] = set of vertices in block i
// is_cut[v] = true if v is a cut vertex
// Assign node IDs: blocks get IDs 0..num_blocks-1
//   cut vertices get IDs num_blocks..num_blocks+num_cuts-1

vector<vector<int>> tree(num_blocks + num_cuts);
for (int i = 0; i < num_blocks; i++) {
    for (int v : blocks[i]) {
        if (is_cut[v]) {
            int cut_id = num_blocks + cut_index[v];
            tree[i].push_back(cut_id);
            tree[cut_id].push_back(i);
        }
    }
}

2-Edge-Connected Components

A graph is 2-edge-connected if it remains connected after removing any single edge. The 2-edge-connected components are the maximal subgraphs that satisfy this property.

The relationship to bridges is direct: two vertices are in the same 2-edge-connected component if and only if every path between them avoids all bridges. Equivalently, if you remove all bridges from the graph, the remaining connected components are exactly the 2-edge-connected components.

Bridges vs. APs in component decomposition:
  • Removing bridges gives 2-edge-connected components. An edge belongs to exactly one component.
  • Removing around APs gives biconnected components. An AP can belong to multiple components.
These are different decompositions. In a graph with no bridges, there is only one 2-edge-connected component (the whole graph), but there may be multiple biconnected components if there are APs.

The algorithm for finding 2-edge-connected components is simple once you have all bridges: remove bridges from the edge set, then find connected components in the remaining graph using BFS or DFS. Total time: O(V + E).

// After finding all bridges (stored in set bridge_edges):
vector<int> comp(n, -1);
int num_comp = 0;
for (int u = 0; u < n; u++) {
    if (comp[u] != -1) continue;
    // BFS/DFS using only non-bridge edges
    queue<int> q;
    q.push(u);
    comp[u] = num_comp;
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int w : adj[v]) {
            if (comp[w] == -1 &&
                !bridge_edges.count({min(v,w), max(v,w)})) {
                comp[w] = num_comp;
                q.push(w);
            }
        }
    }
    num_comp++;
}

After collapsing each 2-edge-connected component into a single supernode, the bridges become the edges of a tree. This is called the bridge tree and is the edge-connectivity analogue of the block-cut tree.

Finding All Bridges Online

In a dynamic graph where edges are added one at a time, we may want to maintain the set of bridges without rerunning Tarjan's algorithm from scratch after every insertion.

The online bridge-finding algorithm maintains a forest of 2-edge-connected components using a Disjoint Set Union (DSU) data structure. When a new edge (u, v) is added:

  1. If u and v are in different connected components, the edge is a new bridge. Merge the components.
  2. If u and v are in the same connected component, find the path between them in the current tree. All edges on this path cease to be bridges (because a cycle now exists through them). Merge all 2-edge-connected components along this path.

The amortized time per edge insertion is O(alpha(n)) where alpha is the inverse Ackermann function. This is nearly O(1) per operation, making it practical for competitive programming problems with online queries.

Limitation: The online algorithm supports edge additions only, not deletions. Fully dynamic bridge maintenance (supporting both additions and deletions) requires more complex data structures such as Euler tour trees and has O(log^2 n) amortized time per operation.

Practice Problems

The following problems test your understanding of bridges, articulation points, and the structures built on top of them. They are roughly ordered from direct application to problems requiring deeper insight.

Problem Platform Difficulty Key Insight
Critical Connections in a Network (#1192) LeetCode Hard Direct bridge finding with Tarjan's algorithm.
Redundant Connection (#684) LeetCode Medium Find the edge that, when removed, leaves a tree. Cycle detection with DSU.
Network Critical Points Various Medium Find all articulation points. Direct application of the AP algorithm.
SUBMERGE SPOJ Medium Count articulation points in a city network. Handle multiple connected components.
EC_P (Euler Circuit Problem) SPOJ Medium Count bridges. If the bridge count is zero and the graph is connected, an Euler circuit exists.
Graph Connectivity Codeforces Hard Use 2-edge-connected component decomposition to reduce the graph, then solve on the bridge tree.
Does Removing a Vertex Disconnect? Classic Easy Check if a given vertex is an articulation point. Single DFS.
Count Biconnected Components Classic Medium Augment the AP algorithm with an edge stack to extract all blocks.
Block-Cut Tree Queries Competitive Hard Build the block-cut tree, then answer path or subtree queries on it using LCA.
Strategy for bridge and AP problems: Start by identifying whether the problem asks about edge connectivity (bridges, 2-edge-connected components, bridge tree) or vertex connectivity (APs, biconnected components, block-cut tree). This determines which decomposition to use.

Time and Space Complexity Summary

All the algorithms discussed in this series run in linear time. The table below summarizes the complexities for a graph with V vertices and E edges.

Algorithm Time Space Notes
Bridge finding (Tarjan) O(V + E) O(V + E) Single DFS with disc and low arrays
AP finding (Tarjan) O(V + E) O(V + E) Single DFS, slightly different condition from bridges
Biconnected components O(V + E) O(V + E) DFS with edge stack. The sum of component sizes can exceed E due to shared APs.
Block-cut tree O(V + E) O(V + E) Built from biconnected component decomposition. Tree has at most V nodes.
2-edge-connected components O(V + E) O(V + E) Remove bridges then find connected components.
Bridge tree O(V + E) O(V + E) Collapse 2-edge-connected components into supernodes.
Online bridge finding O(E · alpha(V)) O(V + E) DSU-based. Amortized nearly O(1) per edge insertion.

The linear time complexity makes these algorithms practical for competitive programming constraints of up to 10^5 or 10^6 vertices and edges. The space overhead is dominated by the adjacency list representation of the graph itself.

Further Reading

Bridges and articulation points deal with connectivity under removal. The natural extension is to study connectivity under edge direction:

Series summary: We started with the DFS tree and the bridge condition (low[v] > disc[u]), moved to the AP condition (low[v] >= disc[u]), and built up to biconnected components, block-cut trees, and 2-edge-connected components. All of these are rooted in a single idea: the DFS tree reveals the back edges, and back edges reveal the cycles that make a graph resilient to failure.
← Articulation Points Back to Series →