Connected Components in an Undirected Graph
Last Updated :
16 Apr, 2025
Given an undirected graph, the task is to return all the connected components in any order.
Examples:
Input: Consider the following graph
Example of an undirected graphOutput: [[0, 1, 2], [3, 4]]
Explanation: There are 2 different connected components.
They are {0, 1, 2} and {3, 4}.
Approach 1: Using Depth first search (DFS)
The main idea is to Use DFS to explore each node. If a node hasn’t been visited yet, start a DFS from it. All nodes reached during this traversal belong to the same connected component.
Step by Step implementation:
- Create an adjacency list from the edge list.
- Initialize a visited array and mark every node as unvisited initially.
- Start from any unvisited node and perform a DFS. All nodes reachable from this node will belong to the same connected component.
- Iterate over all nodes, and for each unvisited node, perform a DFS and store the result.
C++
#include <iostream>
#include <vector>
using namespace std;
// Function to build the adjacency list from edge list
vector<vector<int>> buildGraph(int V, vector<vector<int>>& edges) {
vector<vector<int>> adj(V);
// Populate the adjacency list from the edge list
for (auto edge : edges) {
int u = edge[0];
int v = edge[1];
// Add both directions since the graph is undirected
adj[u].push_back(v);
adj[v].push_back(u);
}
return adj;
}
// Function to perform DFS and collect all
// nodes in the current connected component
void dfs(int node, vector<vector<int>>& adj,
vector<bool>& vis, vector<int>& component) {
// Mark the current node as visited
vis[node] = true;
// Add the node to the current component
component.push_back(node);
// Traverse all unvisited neighbors
for (int neighbor : adj[node]) {
if (!vis[neighbor]) {
dfs(neighbor, adj, vis, component);
}
}
}
// Function to find all connected components
// in an undirected graph
vector<vector<int>> getComponents(int V, vector<vector<int>>& edges) {
// Create the graph using the adjacency list
vector<vector<int>> adj = buildGraph(V, edges);
// Initialize a visited array to keep track of visited nodes
vector<bool> vis(V, false);
// This will store all the connected components
vector<vector<int>> res;
// Iterate through all nodes
for (int i = 0; i < V; ++i) {
// If the node has not been visited, it's a new component
if (!vis[i]) {
vector<int> component;
// Perform DFS to collect all nodes in this component
dfs(i, adj, vis, component);
// Add the component to the result list
res.push_back(component);
}
}
// Return the list of all connected components
return res;
}
int main() {
// Number of nodes in the graph
int V = 5;
// Edge list representing the undirected graph as vector of vectors
vector<vector<int>> edges = {{0, 1}, {1, 2}, {3, 4}};
// Get all connected components using the countComponents function
vector<vector<int>> res = getComponents(V, edges);
for (const auto& comp : res) {
for (int node : comp) {
cout << node << " ";
}
cout << "\n";
}
return 0;
}
Java
import java.util.*;
class GfG {
// Function to build the adjacency list from edge list
static ArrayList<ArrayList<Integer>> buildGraph(int V, int[][] edges) {
ArrayList<ArrayList<Integer>> adj = new ArrayList<>();
// Initialize the adjacency list with empty lists
for (int i = 0; i < V; i++) {
adj.add(new ArrayList<>());
}
// Populate the adjacency list from the edge list
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
// Add both directions since the graph is undirected
adj.get(u).add(v);
adj.get(v).add(u);
}
return adj;
}
// Function to perform DFS and collect all
// nodes in the current connected component
static void dfs(int node, ArrayList<ArrayList<Integer>> adj,
boolean[] vis, ArrayList<Integer> component) {
// Mark the current node as visited
vis[node] = true;
// Add the node to the current component
component.add(node);
// Traverse all unvisited neighbors
for (int neighbor : adj.get(node)) {
if (!vis[neighbor]) {
dfs(neighbor, adj, vis, component);
}
}
}
// Function to find all connected components
// in an undirected graph
static ArrayList<ArrayList<Integer>> getComponents(int V, int[][] edges) {
// Create the graph using the adjacency list
ArrayList<ArrayList<Integer>> adj = buildGraph(V, edges);
// Initialize a visited array to keep track of visited nodes
boolean[] vis = new boolean[V];
// This will store all the connected components
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
// Iterate through all nodes
for (int i = 0; i < V; i++) {
// If the node has not been visited, it's a new component
if (!vis[i]) {
ArrayList<Integer> component = new ArrayList<>();
// Perform DFS to collect all nodes in this component
dfs(i, adj, vis, component);
// Add the component to the result list
res.add(component);
}
}
return res;
}
public static void main(String[] args) {
// Number of nodes in the graph
int V = 5;
// Edge list representing the undirected graph as 2D array
int[][] edges = {
{0, 1},
{1, 2},
{3, 4}
};
// Get all connected components using the getComponents function
ArrayList<ArrayList<Integer>> res = getComponents(V, edges);
// Print each connected component
for (ArrayList<Integer> comp : res) {
for (int node : comp) {
System.out.print(node + " ");
}
System.out.println();
}
}
}
Python
def buildGraph(n, edges):
adj = [[] for _ in range(V)]
# Populate the adjacency list from the edge list
for edge in edges:
u, v = edge[0], edge[1]
# Add both directions since the graph is undirected
adj[u].append(v)
adj[v].append(u)
return adj
def dfs(node, adj, vis, component):
# Mark the current node as visited
vis[node] = True
# Add the node to the current component
component.append(node)
# Traverse all unvisited neighbors
for neighbor in adj[node]:
if not vis[neighbor]:
dfs(neighbor, adj, vis, component)
def getComponents(V, edges):
# Create the graph using the adjacency list
adj = buildGraph(V, edges)
# Initialize a visited array to keep track of visited nodes
vis = [False] * V
# This will store all the connected components
res = []
# Iterate through all nodes
for i in range(V):
# If the node has not been visited, it's a new component
if not vis[i]:
component = []
# Perform DFS to collect all nodes in this component
dfs(i, adj, vis, component)
# Add the component to the result list
res.append(component)
return res
if __name__ == "__main__":
# Number of nodes in the graph
V = 5
# Edge list representing the undirected graph as 2D array
edges = [
[0, 1],
[1, 2],
[3, 4]
]
# Get all connected components using the countComponents function
res = getComponents(V, edges)
# Print each connected component
for comp in res:
print(" ".join(map(str, comp)))
C#
using System;
using System.Collections.Generic;
class GfG {
// Function to build the adjacency list from edge list
static List<List<int>> buildGraph(int V, int[,] edges) {
List<List<int>> adj = new List<List<int>>();
// Initialize the adjacency list with empty lists for each node
for (int i = 0; i < V; i++) {
adj.Add(new List<int>());
}
// Populate the adjacency list from the edge list
for (int i = 0; i < edges.GetLength(0); i++) {
int u = edges[i, 0];
int v = edges[i, 1];
// Add both directions since the graph is undirected
adj[u].Add(v);
adj[v].Add(u);
}
return adj;
}
// Function to perform DFS and collect all
// nodes in the current connected component
static void dfs(int node, List<List<int>> adj,
bool[] vis, List<int> component) {
// Mark the current node as visited
vis[node] = true;
// Add the node to the current component
component.Add(node);
// Traverse all unvisited neighbors
foreach (int neighbor in adj[node]) {
if (!vis[neighbor]) {
dfs(neighbor, adj, vis, component);
}
}
}
// Function to find all connected components in an undirected graph
public static List<List<int>> getComponents(int V, int[,] edges) {
// Create the graph using the adjacency list
List<List<int>> adj = buildGraph(V, edges);
// Initialize a visited array to keep track of visited nodes
bool[] vis = new bool[V];
// This will store all the connected components
List<List<int>> res = new List<List<int>>();
// Iterate through all nodes
for (int i = 0; i < V; i++) {
// If the node has not been visited, it's a new component
if (!vis[i]) {
List<int> component = new List<int>();
// Perform DFS to collect all nodes in this component
dfs(i, adj, vis, component);
// Add the component to the result list
res.Add(component);
}
}
return res;
}
static void Main() {
// Number of nodes in the graph
int V = 5;
// Edge list representing the undirected graph as 2D array
int[,] edges = {
{0, 1},
{1, 2},
{3, 4}
};
// Get all connected components using the countComponents function
List<List<int>> res = getComponents(V, edges);
// Print each connected component
foreach (var comp in res) {
foreach (int node in comp) {
Console.Write(node + " ");
}
Console.WriteLine();
}
}
}
JavaScript
// Function to build the adjacency list from the edge list
function buildGraph(V, edges) {
let adj = Array.from({ length: V }, () => []);
// Populate the adjacency list from the edge list
for (let i = 0; i < edges.length; i++) {
let u = edges[i][0];
let v = edges[i][1];
// Add both directions since the graph is undirected
adj[u].push(v);
adj[v].push(u);
}
return adj;
}
// Function to perform DFS and collect
// all nodes in the current connected component
function dfs(node, adj, vis, component) {
// Mark the current node as visited
vis[node] = true;
// Add the node to the current component
component.push(node);
// Traverse all unvisited neighbors
for (let neighbor of adj[node]) {
if (!vis[neighbor]) {
dfs(neighbor, adj, vis, component);
}
}
}
// Function to find all connected components in an undirected graph
function getComponents(V, edges) {
// Create the graph using the adjacency list
let adj = buildGraph(V, edges);
// Initialize a visited array to keep track of visited nodes
let vis = new Array(V).fill(false);
// This will store all the connected components
let res = [];
// Iterate through all nodes
for (let i = 0; i < V; i++) {
// If the node has not been visited, it's a new component
if (!vis[i]) {
let component = [];
// Perform DFS to collect all nodes in this component
dfs(i, adj, vis, component);
// Add the component to the result list
res.push(component);
}
}
return res;
}
// Driver Code
let V = 5;
// Edge list representing the undirected graph as 2D array
let edges = [
[0, 1],
[1, 2],
[3, 4]
];
// Get all connected components using the countComponents function
let components = getComponents(V, edges);
// Print each connected component
components.forEach(component => {
console.log(component.join(" "));
});
Time Complexity: O(V + E) where V is the number of vertices and E is the number of edges.
Auxiliary Space: O(V + E)
The idea to solve the problem using DSU (Disjoint Set Union) is to initially declare all the nodes as individual subsets and then visit them. When a new unvisited node is encountered, unite it with the underlying subset. In this manner, a single component will be visited in each traversal
Step by Step implementation:
- Declare an array arr[] of size V, where V is the total number of nodes in the graph.
- The value at each index i of the array represents the parent of the node i.
- At the beginning, each node is its own parent. This is the base state where each node is considered an individual subset.
- Perform Union Operation:
- When nodes are united, change the parent of one node to the other, ensuring that the sets are merged properly.
- Each time two nodes are united, their parents are updated accordingly to reflect the new structure of the set.
- Traverse the Nodes:
- Go through each node from 0 to V-1.
- If a node is its own parent (i.e., it is a representative of a set), perform the DSU (find and union) operation starting from that node.
- Store the result of every component and return it.
C++
#include <bits/stdc++.h>
using namespace std;
// Function to find the root parent of a node with path compression
int findParent(vector<int>& parent, int x) {
if (parent[x] == x)
return x;
return parent[x] = findParent(parent, parent[x]);
}
// Function to unite two subsets
void unionSets(vector<int>& parent, int x, int y) {
int px = findParent(parent, x);
int py = findParent(parent, y);
if (px != py) {
parent[px] = py;
}
}
// Function to find all connected components using DSU
vector<vector<int>> getComponents(int V, vector<vector<int>>& edges) {
// Initialize each node as its own parent
vector<int> parent(V);
for (int i = 0; i < V; i++) {
parent[i] = i;
}
// Union sets using edge list
for (auto& edge : edges) {
unionSets(parent, edge[0], edge[1]);
}
// Apply path compression for all nodes
for (int i = 0; i < V; i++) {
parent[i] = findParent(parent, i);
}
// Group nodes by their root parent
unordered_map<int, vector<int>> resMap;
for (int i = 0; i < V; i++) {
resMap[parent[i]].push_back(i);
}
// Collect all components into a result vector
vector<vector<int>> res;
for (auto& entry : resMap) {
res.push_back(entry.second);
}
return res;
}
int main() {
int V = 5;
// Edge list as vector of vectors
vector<vector<int>> edges = {
{0, 1},
{1, 2},
{3, 4}
};
// Find connected components using DSU
vector<vector<int>> res = getComponents(V, edges);
for (const auto& comp : res) {
for (int node : comp) {
cout << node << " ";
}
cout << endl;
}
return 0;
}
Java
import java.util.*;
class GfG {
// Function to find the root parent of a node with path compression
static int findParent(ArrayList<Integer> parent, int x) {
if (parent.get(x) == x)
return x;
parent.set(x, findParent(parent, parent.get(x)));
return parent.get(x);
}
// Function to unite two subsets
static void unionSets(ArrayList<Integer> parent, int x, int y) {
int px = findParent(parent, x);
int py = findParent(parent, y);
if (px != py) {
parent.set(px, py);
}
}
// Function to find all connected components using DSU
static ArrayList<ArrayList<Integer>> getComponents(int V, int[][] edges) {
// Initialize each node as its own parent
ArrayList<Integer> parent = new ArrayList<>();
for (int i = 0; i < V; i++) {
parent.add(i);
}
// Union sets using edge list
for (int[] edge : edges) {
unionSets(parent, edge[0], edge[1]);
}
// Apply path compression for all nodes
for (int i = 0; i < V; i++) {
parent.set(i, findParent(parent, i));
}
// Group nodes by their root parent
Map<Integer, ArrayList<Integer>> resMap = new HashMap<>();
for (int i = 0; i < V; i++) {
resMap.putIfAbsent(parent.get(i), new ArrayList<>());
resMap.get(parent.get(i)).add(i);
}
// Collect all components into a result list
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
for (Map.Entry<Integer, ArrayList<Integer>> entry : resMap.entrySet()) {
res.add(entry.getValue());
}
return res;
}
public static void main(String[] args) {
int V = 5;
// Edge list as 2D array
int[][] edges = {
{0, 1},
{1, 2},
{3, 4}
};
// Find connected components using DSU
ArrayList<ArrayList<Integer>> res = getComponents(V, edges);
// Print the connected components
for (ArrayList<Integer> comp : res) {
for (int node : comp) {
System.out.print(node + " ");
}
System.out.println();
}
}
}
Python
# Function to find the root parent of a node with path compression
def findParent(parent, x):
if parent[x] == x:
return x
# Path compression
parent[x] = findParent(parent, parent[x])
return parent[x]
# Function to unite two subsets
def unionSets(parent, x, y):
px = findParent(parent, x)
py = findParent(parent, y)
if px != py:
# Union operation
parent[px] = py
def getComponents(V, edges):
# Initialize each node as its own parent
parent = [i for i in range(V)]
# Union sets using the edge list
for edge in edges:
unionSets(parent, edge[0], edge[1])
# Apply path compression for all nodes
for i in range(V):
parent[i] = findParent(parent, i)
# Group nodes by their root parent
resMap = {}
for i in range(V):
root = parent[i]
if root not in resMap:
resMap[root] = []
resMap[root].append(i)
# Collect all components into a result list
res = list(resMap.values())
return res
if __name__ == "__main__":
V = 5
# Edge list as 2D array (list of lists)
edges = [
[0, 1],
[1, 2],
[3, 4]
]
# Find connected components using DSU
res = getComponents(V, edges)
# Print connected components
for comp in res:
print(" ".join(map(str, comp)))
C#
using System;
using System.Collections.Generic;
class GfG {
// Function to find the root parent of a node with path compression
public static int findParent(List<int> parent, int x) {
if (parent[x] == x)
return x;
// Path compression
parent[x] = findParent(parent, parent[x]);
return parent[x];
}
// Function to unite two subsets
public static void unionSets(List<int> parent, int x, int y) {
int px = findParent(parent, x);
int py = findParent(parent, y);
if (px != py) {
// Union operation
parent[px] = py;
}
}
// Function to find all connected components using DSU
public static List<List<int>> getComponents(int V, int[,] edges) {
// Initialize each node as its own parent
List<int> parent = new List<int>(V);
for (int i = 0; i < V; i++) {
parent.Add(i);
}
// Union sets using the edge list
for (int i = 0; i < edges.GetLength(0); i++) {
unionSets(parent, edges[i, 0], edges[i, 1]);
}
// Apply path compression for all nodes
for (int i = 0; i < V; i++) {
parent[i] = findParent(parent, i);
}
// Group nodes by their root parent
Dictionary<int, List<int>> resMap = new Dictionary<int, List<int>>();
for (int i = 0; i < V; i++) {
int root = parent[i];
if (!resMap.ContainsKey(root)) {
resMap[root] = new List<int>();
}
resMap[root].Add(i);
}
// Collect all components into a result list
List<List<int>> res = new List<List<int>>();
foreach (var entry in resMap) {
res.Add(entry.Value);
}
return res;
}
static void Main(string[] args) {
int V = 5;
// Edge list as a 2D array
int[,] edges = {
{0, 1},
{1, 2},
{3, 4}
};
// Find connected components using DSU
List<List<int>> res = getComponents(V, edges);
// Print connected components
foreach (var comp in res) {
foreach (int node in comp) {
Console.Write(node + " ");
}
Console.WriteLine();
}
}
}
JavaScript
// Function to find the root parent of a node with path compression
function findParent(parent, x) {
if (parent[x] === x)
return x;
// Path compression
parent[x] = findParent(parent, parent[x]);
return parent[x];
}
// Function to unite two subsets
function unionSets(parent, x, y) {
let px = findParent(parent, x);
let py = findParent(parent, y);
if (px !== py) {
// Union operation
parent[px] = py;
}
}
function getComponents(V, edges) {
// Initialize each node as its own parent
let parent = [];
for (let i = 0; i < V; i++) {
parent[i] = i;
}
// Union sets using the edge list
for (let i = 0; i < edges.length; i++) {
unionSets(parent, edges[i][0], edges[i][1]);
}
// Apply path compression for all nodes
for (let i = 0; i < V; i++) {
parent[i] = findParent(parent, i);
}
// Group nodes by their root parent
let resMap = {};
for (let i = 0; i < V; i++) {
let root = parent[i];
if (!(root in resMap)) {
resMap[root] = [];
}
resMap[root].push(i);
}
// Collect all components into a result array
let res = [];
for (let key in resMap) {
res.push(resMap[key]);
}
return res;
}
// Driver code
let V = 5;
// Edge list as 2D array
let edges = [
[0, 1],
[1, 2],
[3, 4]
];
// Find connected components using DSU
let res = getComponents(V, edges);
// Print connected components
for (let i = 0; i < res.length; i++) {
console.log(res[i].join(' '));
}
Time Complexity: O(V+E)
Auxiliary Space: O(V)
Similar Reads
Depth First Search or DFS for a Graph In Depth First Search (or DFS) for a graph, we traverse all adjacent vertices one by one. When we traverse an adjacent vertex, we completely finish the traversal of all vertices reachable through that adjacent vertex. This is similar to a tree, where we first completely traverse the left subtree and
13 min read
DFS in different language
Iterative Depth First Traversal of Graph Given a directed Graph, the task is to perform Depth First Search of the given graph.Note: Start DFS from node 0, and traverse the nodes in the same order as adjacency list.Note : There can be multiple DFS traversals of a graph according to the order in which we pick adjacent vertices. Here we pick
10 min read
Applications, Advantages and Disadvantages of Depth First Search (DFS) Depth First Search is a widely used algorithm for traversing a graph. Here we have discussed some applications, advantages, and disadvantages of the algorithm. Applications of Depth First Search:1. Detecting cycle in a graph: A graph has a cycle if and only if we see a back edge during DFS. So we ca
4 min read
Difference between BFS and DFS Breadth-First Search (BFS) and Depth-First Search (DFS) are two fundamental algorithms used for traversing or searching graphs and trees. This article covers the basic difference between Breadth-First Search and Depth-First Search.Difference between BFS and DFSParametersBFSDFSStands forBFS stands fo
2 min read
Depth First Search or DFS for disconnected Graph Given a Disconnected Graph, the task is to implement DFS or Depth First Search Algorithm for this Disconnected Graph. Example: Input: Disconnected Graph Output: 0 1 2 3 Algorithm for DFS on Disconnected Graph:In the post for Depth First Search for Graph, only the vertices reachable from a given sour
7 min read
Printing pre and post visited times in DFS of a graph Depth First Search (DFS) marks all the vertices of a graph as visited. So for making DFS useful, some additional information can also be stored. For instance, the order in which the vertices are visited while running DFS. Pre-visit and Post-visit numbers are the extra information that can be stored
8 min read
Tree, Back, Edge and Cross Edges in DFS of Graph Given a directed graph, the task is to identify tree, forward, back and cross edges present in the graph.Note: There can be multiple answers.Example:Input: GraphOutput:Tree Edges: 1->2, 2->4, 4->6, 1->3, 3->5, 5->7, 5->8 Forward Edges: 1->8 Back Edges: 6->2 Cross Edges: 5-
9 min read
Transitive Closure of a Graph using DFS Given a directed graph, find out if a vertex v is reachable from another vertex u for all vertex pairs (u, v) in the given graph. Here reachable means that there is a path from vertex u to v. The reach-ability matrix is called transitive closure of a graph. For example, consider below graph: GraphTr
8 min read
Variations of DFS implementations