How to output all biconnected components of an undirected graph?
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Biconnected components describe the blocks of an undirected graph that do not fall apart when you remove one internal vertex. They are useful in network reliability, articulation-point analysis, and graph decomposition.
To output all of them, the standard tool is a depth-first search with discovery times, low-link values, and a stack of traversed edges. The stack is what lets you emit the actual component contents instead of only counting them.
Core Idea: DFS, disc, and low
During DFS, assign each vertex a discovery time disc[u]. Also compute low[u], the earliest discovery time reachable from u through tree edges and at most one back edge.
For an undirected edge from u to v:
- if
vis unvisited, it becomes a DFS child - if
vis already visited and is not the parent, it is a back edge
The key condition is this:
- when
low[v] >= disc[u], the edges on the stack from the most recent boundary up to(u, v)form one biconnected component
That happens because v and everything below it can no longer reach an ancestor of u without going through u.
Why an Edge Stack Is Needed
If you only compute articulation points, you can stop after maintaining disc and low. To output the components themselves, keep a stack of edges visited during DFS.
Whenever you traverse a tree edge or discover a back edge to an ancestor, push that edge. When low[v] >= disc[u], pop until (u, v) appears. Everything popped belongs to the same block.
This naturally handles cycles, shared articulation vertices, and bridge-like single-edge blocks.
Python Implementation
The following implementation outputs each component as a list of edges:
For that graph, one component is the triangle among 0, 1, and 2, another is the cycle among 3, 4, and 5, and the edge from 1 to 3 forms its own block.
Converting Edge Blocks to Vertex Sets
Sometimes you want vertices rather than edges. That is easy once a component has been popped.
You can apply that helper to each emitted component:
Remember that articulation vertices can appear in more than one component. That is expected behavior, not a bug.
Complexity
The DFS visits each vertex once and examines each undirected edge a constant number of times. Time complexity is O(V + E), and the auxiliary memory is also O(V + E) because of the recursion state, arrays, and edge stack.
That is optimal for outputting all components, since reading the graph already costs O(V + E).
Common Pitfalls
The most common mistake is confusing bridges with biconnected components. A bridge edge can become a single-edge block in the decomposition, but the algorithm still uses the articulation condition low[v] >= disc[u] and a stack of edges.
Another bug is pushing too many back edges. In an undirected graph, only push a back edge when it goes to an ancestor. A simple check like disc[v] < disc[u] prevents duplicate handling.
Disconnected graphs are also easy to miss. You must start DFS from every unvisited vertex, not only vertex 0.
Finally, many implementations forget to flush the edge stack after finishing a connected component. If edges remain on the stack after one DFS root completes, they belong to one final block and should be emitted.
Summary
- Use DFS with discovery times and low-link values.
- Keep a stack of traversed edges so you can output actual components.
- Emit a component when
low[v] >= disc[u]. - Articulation vertices may belong to multiple components.
- Handle disconnected graphs by starting DFS from every unvisited vertex.
- The full algorithm runs in
O(V + E)time.
Related reading
- How to parallelize stochastic gradient descent?
- How to partition an array of integers in a way that minimizes the maximum of the sum of each partition?
- How to perform K-swap operations on an N-digit integer to get maximum possible number
- How to picture for loop in block representation of algorithm
- How to Partition a Queue in a distributed system
- How to peek at messages in the queue
- How to permutate tranposition in tensorflow?
- How to print all possible balanced parentheses for an expression?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.