Given a undirected Graph consisting of V vertices numbered from 0 to V-1 and E edges. The ith edge is represented by [ai,bi], denoting a edge between vertex ai and bi. We say two vertices u and v belong to a same component if there is a path from u to v or v to u. Find the number of connected components in the graph.
A connected component is a subgraph of a graph in which there exists a path between any two vertices, and no vertex of the subgraph shares an edge with a vertex outside of the subgraph.
Input: V=4, edges=[[0,1],[1,2]]
Output: 2
Explanation: Vertices {0,1,2} forms the first component and vertex 3 forms the second component.
Input: V = 7, edges = [[0, 1], [1, 2], [2, 3], [4, 5]]
Output: 3
Explanation:
The edges [0, 1], [1, 2], [2, 3] form a connected component with vertices {0, 1, 2, 3}.
The edge [4, 5] forms another connected component with vertices {4, 5}.
Therefore, the graph has 3 connected components: {0, 1, 2, 3}, {4, 5}, and the isolated vertices {6} (vertices 6 and any other unconnected vertices).
Input: V = 5, edges = [[0, 1], [1, 2], [3, 4]]
A component is like a group of directly or indirectly connected nodes and no other nodes outside of the group. Every node in a component can be visited from every other node in the same province.
To solve this problem, all the nodes and their connections can be explored to identify these groups.
For this, the traversal techniques BFS and DFS can be used and every time, we had to start from a new node, a new group of nodes(component) is explored.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
// Function for BFS traversal
void bfs(int node, vector<int> adjLs[], int vis[]) {
// Mark the node as visited
vis[node] = 1;
// Queue required for BFS traversal
queue <int> q;
// To start traversal from node
q.push(node);
/* Keep on traversing till
the queue is not empty */
while(!q.empty()) {
// Get the node
int i = q.front();
q.pop();
// Traverse its unvisited neighbours
for(auto adjNodes: adjLs[i]) {
if(vis[adjNodes] != 1) {
// Mark the node as visited
vis[adjNodes] = 1;
// Add the node to queue
q.push(adjNodes);
}
}
}
}
// Function for DFS traversal
void dfs(int node, vector<int> adjLs[], int vis[]) {
// Mark the node as visited
vis[node] = 1;
// Traverse its unvisited neighbours
for(auto it: adjLs[node]) {
if(!vis[it]) {
// Recursively perform DFS
dfs(it, adjLs, vis);
}
}
}
public:
/* Function call to find the number of
connected components in the given graph */
int findNumberOfComponent(int E, int V,
vector<vector<int>> &edges) {
// To store adjacency list
vector<int> adjLs[V];
// Add edges to adjacency list
for(int i=0; i < E; i++) {
adjLs[edges[i][0]].push_back(edges[i][1]);
adjLs[edges[i][1]].push_back(edges[i][0]);
}
// Visited array
int vis[V] = {0};
// Variable to store number of components
int cnt = 0;
// Start Traversal
for(int i=0; i < V; i++) {
// If the node is not visited
if(!vis[i]) {
// Increment counter
cnt++;
/* Start traversal from current
node using any traversal */
bfs(i, adjLs, vis);
//dfs(i, adjLs, vis);
}
}
// Return the count
return cnt;
}
};
int main() {
int V = 4, E = 2;
vector<vector<int>> edges = {
{0, 1},
{1, 2}
};
/* Creating an instance of
Solution class */
Solution sol;
/* Function call to find the number of
connected components in the given graph */
int ans = sol.findNumberOfComponent(E, V, edges);
cout << "The number of components in the given graph is: " << ans;
return 0;
}
import java.util.*;
class Solution {
// Function for BFS traversal
private void bfs(int node, List<Integer>[] adjLs,
boolean[] vis) {
// Mark the node as visited
vis[node] = true;
// Queue required for BFS traversal
Queue<Integer> q = new LinkedList<>();
// To start traversal from node
q.add(node);
/* Keep on traversing till
the queue is not empty */
while (!q.isEmpty()) {
// Get the node
int i = q.poll();
// Traverse its unvisited neighbours
for (int adjNodes : adjLs[i]) {
if (!vis[adjNodes]) {
// Mark the node as visited
vis[adjNodes] = true;
// Add the node to queue
q.add(adjNodes);
}
}
}
}
// Function for DFS traversal
private void dfs(int node, List<Integer>[] adjLs,
boolean[] vis) {
// Mark the node as visited
vis[node] = true;
// Traverse its unvisited neighbours
for (int it : adjLs[node]) {
if (!vis[it]) {
// Recursively perform DFS
dfs(it, adjLs, vis);
}
}
}
/* Function call to find the number of
connected components in the given graph */
public int findNumberOfComponent(int E, int V,
List<List<Integer>> edges) {
// To store adjacency list
List<Integer>[] adjLs = new ArrayList[V];
for (int i = 0; i < V; i++) {
adjLs[i] = new ArrayList<>();
}
// Add edges to adjacency list
for (int i = 0; i < E; i++) {
adjLs[edges.get(i).get(0)].add(edges.get(i).get(1));
adjLs[edges.get(i).get(1)].add(edges.get(i).get(0));
}
// Visited array
boolean[] vis = new boolean[V];
// Variable to store number of components
int cnt = 0;
// Start Traversal
for (int i = 0; i < V; i++) {
// If the node is not visited
if (!vis[i]) {
// Increment counter
cnt++;
/* Start traversal from current
node using any traversal */
bfs(i, adjLs, vis);
// dfs(i, adjLs, vis);
}
}
// Return the count
return cnt;
}
public static void main(String[] args) {
int V = 4, E = 2;
List<List<Integer>> edges = Arrays.asList(
Arrays.asList(0, 1),
Arrays.asList(1, 2)
);
/* Creating an instance of
Solution class */
Solution sol = new Solution();
/* Function call to find the number of
connected components in the given graph */
int ans = sol.findNumberOfComponent(E, V, edges);
System.out.println("The number of components in the given graph is: " + ans);
}
}
from collections import deque
class Solution:
# Function for BFS traversal
def bfs(self, node, adjLs, vis):
# Mark the node as visited
vis[node] = 1
# Queue required for BFS traversal
q = deque()
# To start traversal from node
q.append(node)
# Keep on traversing till
# the queue is not empty
while q:
# Get the node
i = q.popleft()
# Traverse its unvisited neighbours
for adjNodes in adjLs[i]:
if vis[adjNodes] != 1:
# Mark the node as visited
vis[adjNodes] = 1
# Add the node to queue
q.append(adjNodes)
# Function for DFS traversal
def dfs(self, node, adjLs, vis):
# Mark the node as visited
vis[node] = 1
# Traverse its unvisited neighbours
for it in adjLs[node]:
if not vis[it]:
# Recursively perform DFS
self.dfs(it, adjLs, vis)
# Function call to find the number of
# connected components in the given graph
def findNumberOfComponent(self, E, V, edges):
# To store adjacency list
adjLs = [[] for _ in range(V)]
# Add edges to adjacency list
for i in range(E):
adjLs[edges[i][0]].append(edges[i][1])
adjLs[edges[i][1]].append(edges[i][0])
# Visited array
vis = [0] * V
# Variable to store number of components
cnt = 0
# Start Traversal
for i in range(V):
# If the node is not visited
if not vis[i]:
# Increment counter
cnt += 1
# Start traversal from current
# node using any traversal
self.bfs(i, adjLs, vis)
# self.dfs(i, adjLs, vis)
# Return the count
return cnt
if __name__ == "__main__":
V = 4
E = 2
edges = [
[0, 1],
[1, 2]
]
# Creating an instance of
# Solution class
sol = Solution()
# Function call to find the number of
# connected components in the given graph
ans = sol.findNumberOfComponent(E, V, edges)
print("The number of components in the given graph is:", ans)
class Solution {
// Function for BFS traversal
bfs(node, adjLs, vis) {
// Mark the node as visited
vis[node] = 1;
// Queue required for BFS traversal
const q = [];
// To start traversal from node
q.push(node);
/* Keep on traversing till
the queue is not empty */
while (q.length > 0) {
// Get the node
const i = q.shift();
// Traverse its unvisited neighbours
for (const adjNodes of adjLs[i]) {
if (vis[adjNodes] !== 1) {
// Mark the node as visited
vis[adjNodes] = 1;
// Add the node to queue
q.push(adjNodes);
}
}
}
}
// Function for DFS traversal
dfs(node, adjLs, vis) {
// Mark the node as visited
vis[node] = 1;
// Traverse its unvisited neighbours
for (const it of adjLs[node]) {
if (!vis[it]) {
// Recursively perform DFS
this.dfs(it, adjLs, vis);
}
}
}
/* Function call to find the number of
connected components in the given graph */
findNumberOfComponent(E, V, edges) {
// To store adjacency list
const adjLs = Array.from({ length: V }, () => []);
// Add edges to adjacency list
for (let i = 0; i < E; i++) {
adjLs[edges[i][0]].push(edges[i][1]);
adjLs[edges[i][1]].push(edges[i][0]);
}
// Visited array
const vis = new Array(V).fill(0);
// Variable to store number of components
let cnt = 0;
// Start Traversal
for (let i = 0; i < V; i++) {
// If the node is not visited
if (!vis[i]) {
// Increment counter
cnt++;
/* Start traversal from current
node using any traversal */
this.bfs(i, adjLs, vis);
// this.dfs(i, adjLs, vis);
}
}
// Return the count
return cnt;
}
}
const main = () => {
const V = 4;
const E = 2;
const edges = [
[0, 1],
[1, 2]
];
// Creating an instance of
// Solution class
const sol = new Solution();
// Function call to find the number of
// connected components in the given graph
const ans = sol.findNumberOfComponent(E, V, edges);
console.log("The number of components in the given graph is:", ans);
};
main();
Time Complexity: O(V + E) (where V denotes the number of nodes, E denotes the number of edges)
Space Complexity: O(V + E)
Q: Why does DFS/BFS correctly count the number of components?
A: It visits all nodes in a component before moving to another, ensuring each DFS/BFS call represents one full component.
Q: What if the graph is disconnected?
A: The algorithm still works; each disconnected part will be counted as a separate component.
Q: How would this change if the graph was stored as an edge list instead of an adjacency list?
A: Convert the edge list into an adjacency list before performing DFS/BFS.
Q: How would you optimize Union-Find for large graphs?
A: Use Path Compression and Union by Rank to speed up the merging process to nearly O(1) per operation.