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