Number of Connected Components in an Undirected Graph medium
Problem Statement
You have been given an undirected graph represented as an integer array n
representing the number of nodes and a 2D integer array edges
where each edges[i] = [u, v]
indicates that there is an undirected edge between nodes u
and v
. You are tasked to find the number of connected components in the graph. A connected component is a subset of the graph where every node is reachable from every other node within the subset.
Example 1
Input: n = 5, edges = [[0,1],[1,2],[3,4]] Output: 2 Explanation: There are two connected components: {0,1,2} and {3,4}.
Example 2
Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]] Output: 1 Explanation: There is only one connected component: {0,1,2,3,4}.
Steps
-
Initialization: Create an adjacency list to represent the graph. Initialize a
visited
set to keep track of visited nodes. The number of connected components is initialized to 0. -
Depth-First Search (DFS): Iterate through each node in the graph. If a node hasn't been visited, perform a DFS starting from that node. The DFS will explore all reachable nodes from the current node, marking them as visited. Each time a DFS is initiated from an unvisited node, it signifies a new connected component.
-
Counting Components: Increment the count of connected components after each DFS call.
-
Return: Return the final count of connected components.
Explanation
The algorithm uses Depth-First Search (DFS) to explore the graph. DFS systematically visits all nodes reachable from a starting node. By starting a DFS from each unvisited node, we ensure that all connected components are identified. Each time a DFS is initiated from an unvisited node, it means we've found a new unconnected part of the graph.
Code
def countComponents(n, edges):
"""
Counts the number of connected components in an undirected graph.
Args:
n: The number of nodes in the graph.
edges: A list of edges, where each edge is a list of two node indices.
Returns:
The number of connected components in the graph.
"""
adj_list = [[] for _ in range(n)] # Adjacency list to represent the graph
for u, v in edges:
adj_list[u].append(v)
adj_list[v].append(u) # Undirected graph, add edge in both directions
visited = set() # Keep track of visited nodes
count = 0 # Count of connected components
def dfs(node):
visited.add(node)
for neighbor in adj_list[node]:
if neighbor not in visited:
dfs(neighbor)
for i in range(n):
if i not in visited:
dfs(i)
count += 1
return count
Complexity
-
Time Complexity: O(V + E), where V is the number of vertices (nodes) and E is the number of edges. This is because the DFS visits each node and edge at most once.
-
Space Complexity: O(V), dominated by the
visited
set and the adjacency list. In the worst-case scenario (a complete graph), the adjacency list will store V * (V-1) / 2 entries. However, on average and for sparse graphs, it's approximately O(V).