
- DSA - Home
- DSA - Overview
- DSA - Environment Setup
- DSA - Algorithms Basics
- DSA - Asymptotic Analysis
- Data Structures
- DSA - Data Structure Basics
- DSA - Data Structures and Types
- DSA - Array Data Structure
- DSA - Skip List Data Structure
- Linked Lists
- DSA - Linked List Data Structure
- DSA - Doubly Linked List Data Structure
- DSA - Circular Linked List Data Structure
- Stack & Queue
- DSA - Stack Data Structure
- DSA - Expression Parsing
- DSA - Queue Data Structure
- DSA - Circular Queue Data Structure
- DSA - Priority Queue Data Structure
- DSA - Deque Data Structure
- Searching Algorithms
- DSA - Searching Algorithms
- DSA - Linear Search Algorithm
- DSA - Binary Search Algorithm
- DSA - Interpolation Search
- DSA - Jump Search Algorithm
- DSA - Exponential Search
- DSA - Fibonacci Search
- DSA - Sublist Search
- DSA - Hash Table
- Sorting Algorithms
- DSA - Sorting Algorithms
- DSA - Bubble Sort Algorithm
- DSA - Insertion Sort Algorithm
- DSA - Selection Sort Algorithm
- DSA - Merge Sort Algorithm
- DSA - Shell Sort Algorithm
- DSA - Heap Sort Algorithm
- DSA - Bucket Sort Algorithm
- DSA - Counting Sort Algorithm
- DSA - Radix Sort Algorithm
- DSA - Quick Sort Algorithm
- Matrices Data Structure
- DSA - Matrices Data Structure
- DSA - Lup Decomposition In Matrices
- DSA - Lu Decomposition In Matrices
- Graph Data Structure
- DSA - Graph Data Structure
- DSA - Depth First Traversal
- DSA - Breadth First Traversal
- DSA - Spanning Tree
- DSA - Topological Sorting
- DSA - Strongly Connected Components
- DSA - Biconnected Components
- DSA - Augmenting Path
- DSA - Network Flow Problems
- DSA - Flow Networks In Data Structures
- DSA - Edmonds Blossom Algorithm
- DSA - Maxflow Mincut Theorem
- Tree Data Structure
- DSA - Tree Data Structure
- DSA - Tree Traversal
- DSA - Binary Search Tree
- DSA - AVL Tree
- DSA - Red Black Trees
- DSA - B Trees
- DSA - B+ Trees
- DSA - Splay Trees
- DSA - Range Queries
- DSA - Segment Trees
- DSA - Fenwick Tree
- DSA - Fusion Tree
- DSA - Hashed Array Tree
- DSA - K-Ary Tree
- DSA - Kd Trees
- DSA - Priority Search Tree Data Structure
- Recursion
- DSA - Recursion Algorithms
- DSA - Tower of Hanoi Using Recursion
- DSA - Fibonacci Series Using Recursion
- Divide and Conquer
- DSA - Divide and Conquer
- DSA - Max-Min Problem
- DSA - Strassen's Matrix Multiplication
- DSA - Karatsuba Algorithm
- Greedy Algorithms
- DSA - Greedy Algorithms
- DSA - Travelling Salesman Problem (Greedy Approach)
- DSA - Prim's Minimal Spanning Tree
- DSA - Kruskal's Minimal Spanning Tree
- DSA - Dijkstra's Shortest Path Algorithm
- DSA - Map Colouring Algorithm
- DSA - Fractional Knapsack Problem
- DSA - Job Sequencing with Deadline
- DSA - Optimal Merge Pattern Algorithm
- Dynamic Programming
- DSA - Dynamic Programming
- DSA - Matrix Chain Multiplication
- DSA - Floyd Warshall Algorithm
- DSA - 0-1 Knapsack Problem
- DSA - Longest Common Sub-sequence Algorithm
- DSA - Travelling Salesman Problem (Dynamic Approach)
- Hashing
- DSA - Hashing Data Structure
- DSA - Collision In Hashing
- Disjoint Set
- DSA - Disjoint Set
- DSA - Path Compression And Union By Rank
- Heap
- DSA - Heap Data Structure
- DSA - Binary Heap
- DSA - Binomial Heap
- DSA - Fibonacci Heap
- Tries Data Structure
- DSA - Tries
- DSA - Standard Tries
- DSA - Compressed Tries
- DSA - Suffix Tries
- Treaps
- DSA - Treaps Data Structure
- Bit Mask
- DSA - Bit Mask In Data Structures
- Bloom Filter
- DSA - Bloom Filter Data Structure
- Approximation Algorithms
- DSA - Approximation Algorithms
- DSA - Vertex Cover Algorithm
- DSA - Set Cover Problem
- DSA - Travelling Salesman Problem (Approximation Approach)
- Randomized Algorithms
- DSA - Randomized Algorithms
- DSA - Randomized Quick Sort Algorithm
- DSA - Karger’s Minimum Cut Algorithm
- DSA - Fisher-Yates Shuffle Algorithm
- Miscellaneous
- DSA - Infix to Postfix
- DSA - Bellmon Ford Shortest Path
- DSA - Maximum Bipartite Matching
- DSA Useful Resources
- DSA - Questions and Answers
- DSA - Selection Sort Interview Questions
- DSA - Merge Sort Interview Questions
- DSA - Insertion Sort Interview Questions
- DSA - Heap Sort Interview Questions
- DSA - Bubble Sort Interview Questions
- DSA - Bucket Sort Interview Questions
- DSA - Radix Sort Interview Questions
- DSA - Cycle Sort Interview Questions
- DSA - Quick Guide
- DSA - Useful Resources
- DSA - Discussion
Bi-connected Components
Imagine you have a big network of cities (nodes) connected by roads (edges). Now, if you remove just one city, and the whole network breaks into separate parts, then that city is called an articulation point (or cut vertex).
So, Bi-connected Components(BCC) are the largest possible parts of a graph(subgraph) where no single node removal can disconnect them.
For example, consider the following graph:

The articulation points in the graph are:
{2} {4} {6}
The Bi-connected Components (BCCs) in the graph are:
{0, 1, 2, 3} {2, 4} {4, 5} {4, 6} {6, 7, 8}
Properties of Bi-connected Components
Here are some key properties of bi-connected components:
- Edge: Every edge belongs to exactly one biconnected component.
- Articulation Points: A node can have multiple biconnected components, but it can be an articulation point for only one of them.
- Bridge: An edge that, when removed, increases the number of biconnected components.
- If a graph has no articulation points, the whole graph itself is BCC.
Algorithm for Finding BCC
We use DFS(depth first search) to find biconnected components.
Here's a simple algorithm to find biconnected components in a graph:
1. Perform a Depth First Search (DFS) traversal of the graph. 2. During the DFS traversal, maintain a stack to keep track of the nodes visited. 3. For each node, keep track of the discovery time and low value. 4. When visiting a node, push it onto the stack and update the discovery time and low value. 5. If a back edge is found (i.e., an edge to an already visited node), update the low value of the current node. 6. If the low value of the current node is less than or equal to the discovery time of the parent node, pop nodes from the stack until the current node is reached. 7. The popped nodes form a biconnected component.
Let's say we have this graph
A / \ B C | | D - E / \ F G
The edges are:
AB, AC, BD, CE, EF, EG, DE
If we remove node E, the graph look like this:
A / \ B C | D E / \ F G
- {A, B, C, D, E}
- {E, F, G}
A, B, C, and D form one part.
F and G are now completely disconnected from the rest of the graph.
So E is an articulation point.
Biconnected components are −
Code for Biconnected Components
Let's see the code for finding biconnected components in a above graph:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #define MAX 100 int time = 0; int disc[MAX], low[MAX], parent[MAX]; bool visited[MAX]; typedef struct { int u, v; } Edge; Edge stack[MAX]; int top = -1; // Mapping indexes to letters char nodes[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; void push(int u, int v) { stack[++top].u = u; stack[top].v = v; } void popUntil(int u, int v) { printf("\nBiconnected Component: "); while (top >= 0) { printf("{%c, %c} ", nodes[stack[top].u], nodes[stack[top].v]); if (stack[top].u == u && stack[top].v == v) { top--; break; } top--; } } void DFS(int graph[MAX][MAX], int u, int n) { visited[u] = true; disc[u] = low[u] = ++time; for (int v = 0; v < n; v++) { if (graph[u][v]) { if (!visited[v]) { parent[v] = u; push(u, v); DFS(graph, v, n); low[u] = (low[u] < low[v]) ? low[u] : low[v]; if (low[v] >= disc[u]) { popUntil(u, v); } } else if (v != parent[u] && disc[v] < disc[u]) { low[u] = (low[u] < disc[v]) ? low[u] : disc[v]; push(u, v); } } } } void findBCC(int graph[MAX][MAX], int n) { memset(visited, false, sizeof(visited)); memset(parent, -1, sizeof(parent)); memset(disc, 0, sizeof(disc)); memset(low, 0, sizeof(low)); for (int i = 0; i < n; i++) { if (!visited[i]) { DFS(graph, i, n); } } if (top >= 0) { printf("\nRemaining Biconnected Component: "); while (top >= 0) { printf("{%c, %c} ", nodes[stack[top].u], nodes[stack[top].v]); top--; } } } int main() { int n = 7; int graph[MAX][MAX] = {0}; graph[0][1] = graph[1][0] = 1; // A-B graph[0][2] = graph[2][0] = 1; // A-C graph[1][3] = graph[3][1] = 1; // B-D graph[2][4] = graph[4][2] = 1; // C-E graph[4][5] = graph[5][4] = 1; // E-F graph[4][6] = graph[6][4] = 1; // E-G graph[3][4] = graph[4][3] = 1; // D-E printf("Biconnected Components are:\n"); findBCC(graph, n); return 0; }
Output
Following is the output of the above code:
Biconnected Components are: Biconnected Component: {E, F} Biconnected Component: {E, G} Biconnected Component: {C, A} {E, C} {D, E} {B, D} {A, B}
#include <iostream> #include <vector> #include <stack> #include <cstring> using namespace std; #define MAX 100 int timeCounter = 0; int disc[MAX], low[MAX], parent[MAX]; bool visited[MAX]; struct Edge { int u, v; }; stack<Edge> edgeStack; // Mapping indexes to letters char nodes[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; void push(int u, int v) { edgeStack.push({u, v}); } void popUntil(int u, int v) { cout << "\nBiconnected Component: "; while (!edgeStack.empty()) { Edge e = edgeStack.top(); edgeStack.pop(); cout << "{" << nodes[e.u] << ", " << nodes[e.v] << "} "; if (e.u == u && e.v == v) { break; } } } void DFS(vector<int> graph[MAX], int u) { visited[u] = true; disc[u] = low[u] = ++timeCounter; for (int v : graph[u]) { if (!visited[v]) { parent[v] = u; push(u, v); DFS(graph, v); low[u] = min(low[u], low[v]); if (low[v] >= disc[u]) { popUntil(u, v); } } else if (v != parent[u] && disc[v] < disc[u]) { low[u] = min(low[u], disc[v]); push(u, v); } } } void findBCC(vector<int> graph[MAX], int n) { memset(visited, false, sizeof(visited)); memset(parent, -1, sizeof(parent)); memset(disc, 0, sizeof(disc)); memset(low, 0, sizeof(low)); for (int i = 0; i < n; i++) { if (!visited[i]) { DFS(graph, i); } } if (!edgeStack.empty()) { cout << "\nRemaining Biconnected Component: "; while (!edgeStack.empty()) { Edge e = edgeStack.top(); edgeStack.pop(); cout << "{" << nodes[e.u] << ", " << nodes[e.v] << "} "; } } } int main() { int n = 7; vector<int> graph[MAX]; graph[0].push_back(1); graph[1].push_back(0); // A-B graph[0].push_back(2); graph[2].push_back(0); // A-C graph[1].push_back(3); graph[3].push_back(1); // B-D graph[2].push_back(4); graph[4].push_back(2); // C-E graph[4].push_back(5); graph[5].push_back(4); // E-F graph[4].push_back(6); graph[6].push_back(4); // E-G graph[3].push_back(4); graph[4].push_back(3); // D-E cout << "Biconnected Components are:\n"; findBCC(graph, n); return 0; }
Output
Following is the output of the above code:
Biconnected Components are: Biconnected Component: {E, F} Biconnected Component: {E, G} Biconnected Component: {C, A} {E, C} {D, E} {B, D} {A, B}
import java.util.*; public class BiconnectedComponents { static final int MAX = 100; static int time = 0; static int[] disc = new int[MAX], low = new int[MAX], parent = new int[MAX]; static boolean[] visited = new boolean[MAX]; static Stack<int[]> edgeStack = new Stack<>(); // Mapping indexes to letters static char[] nodes = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; static void push(int u, int v) { edgeStack.push(new int[]{u, v}); } static void popUntil(int u, int v) { System.out.print("\nBiconnected Component: "); while (!edgeStack.isEmpty()) { int[] edge = (int[]) edgeStack.pop(); // Explicit cast System.out.print("{" + nodes[edge[0]] + ", " + nodes[edge[1]] + "} "); if (edge[0] == u && edge[1] == v) { break; } } } static void DFS(List<Integer>[] graph, int u) { visited[u] = true; disc[u] = low[u] = ++time; for (int v : graph[u]) { if (!visited[v]) { parent[v] = u; push(u, v); DFS(graph, v); low[u] = Math.min(low[u], low[v]); if (low[v] >= disc[u]) { popUntil(u, v); } } else if (v != parent[u] && disc[v] < disc[u]) { low[u] = Math.min(low[u], disc[v]); push(u, v); } } } static void findBCC(List<Integer>[] graph, int n) { Arrays.fill(visited, false); Arrays.fill(parent, -1); Arrays.fill(disc, 0); Arrays.fill(low, 0); for (int i = 0; i < n; i++) { if (!visited[i]) { DFS(graph, i); } } if (!edgeStack.isEmpty()) { System.out.print("\nRemaining Biconnected Component: "); while (!edgeStack.isEmpty()) { int[] edge = (int[]) edgeStack.pop(); // Explicit cast System.out.print("{" + nodes[edge[0]] + ", " + nodes[edge[1]] + "} "); } } } public static void main(String[] args) { int n = 7; List<Integer>[] graph = new ArrayList[MAX]; for (int i = 0; i < MAX; i++) { graph[i] = new ArrayList<>(); } graph[0].add(1); graph[1].add(0); // A-B graph[0].add(2); graph[2].add(0); // A-C graph[1].add(3); graph[3].add(1); // B-D graph[2].add(4); graph[4].add(2); // C-E graph[4].add(5); graph[5].add(4); // E-F graph[4].add(6); graph[6].add(4); // E-G graph[3].add(4); graph[4].add(3); // D-E System.out.println("Biconnected Components are:"); findBCC(graph, n); } }
Output
Following is the output of the above code:
Biconnected Components are: Biconnected Component: {E, F} Biconnected Component: {E, G} Biconnected Component: {C, A} {E, C} {D, E} {B, D} {A, B}
class BiconnectedComponents: def __init__(self, n): self.n = n self.time = 0 self.graph = [[] for _ in range(n)] self.disc = [-1] * n self.low = [-1] * n self.parent = [-1] * n self.visited = [False] * n self.stack = [] # Mapping indexes to letters self.nodes = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] def add_edge(self, u, v): self.graph[u].append(v) self.graph[v].append(u) def push(self, u, v): self.stack.append((u, v)) def pop_until(self, u, v): print("\nBiconnected Component: ", end="") while self.stack: edge = self.stack.pop() print(f"{{{self.nodes[edge[0]]}, {self.nodes[edge[1]]}}} ", end="") if edge == (u, v): break def dfs(self, u): self.visited[u] = True self.disc[u] = self.low[u] = self.time + 1 self.time += 1 for v in self.graph[u]: if not self.visited[v]: self.parent[v] = u self.push(u, v) self.dfs(v) self.low[u] = min(self.low[u], self.low[v]) if self.low[v] >= self.disc[u]: self.pop_until(u, v) elif v != self.parent[u] and self.disc[v] < self.disc[u]: self.low[u] = min(self.low[u], self.disc[v]) self.push(u, v) def find_bcc(self): for i in range(self.n): if not self.visited[i]: self.dfs(i) if self.stack: print("\nRemaining Biconnected Component: ", end="") while self.stack: edge = self.stack.pop() print(f"{{{self.nodes[edge[0]]}, {self.nodes[edge[1]]}}} ", end="") if __name__ == "__main__": n = 7 bcc = BiconnectedComponents(n) bcc.add_edge(0, 1) # A-B bcc.add_edge(0, 2) # A-C bcc.add_edge(1, 3) # B-D bcc.add_edge(2, 4) # C-E bcc.add_edge(4, 5) # E-F bcc.add_edge(4, 6) # E-G bcc.add_edge(3, 4) # D-E print("Biconnected Components are:") bcc.find_bcc()
Output
Following is the output of the above code:
Biconnected Components are: Biconnected Component: {E, F} Biconnected Component: {E, G} Biconnected Component: {C, A} {E, C} {D, E} {B, D} {A, B}
Time Complexity of BCC
- The time complexity of finding biconnected components using the above algorithm is O(V + E), where V is the number of vertices and E is the number of edges in the graph.
Applications of Biconnected Components
Biconnected components are used in various applications, such as:
- Network Reliability: If you want to know the weakest link in a network, BCC helps.
- Bridges & Roads: Cities connected by roads? Find which roads are critical.
- Computer Networks: In network design, BCC helps in fault tolerance.
- File Systems: Used in distributed storage systems to avoid single points of failure.
Conclusion
We have learned about biconnected components in a graph. We have seen how to find biconnected components using a simple algorithm. We have also seen the code in C, C++, Java, and Python. Biconnected components are essential in network design, fault tolerance, and distributed systems.