You have a graph of n nodes labeled from 0 to n - 1. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph.
Return the number of connected components in the graph.
Example 1:
Input: n = 5, edges = [[0,1],[1,2],[3,4]]
Output: 2
Example 2:
Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]
Output: 1
Constraints:
1 <= n <= 2000
1 <= edges.length <= 5000
edges[i] = [ai, bi]
ai != bi
There are no repeated edges.
Solutions
Solution 1: DFS
Thinking
Count connected components in an undirected graph. Start a search at each unseen node and increment once per new component. \(O(n+m)\) fits the limits.
Build the adjacency list and DFS: return \(0\) if seen, otherwise mark, recurse on neighbors, and return \(1\). Summing over all nodes is the count.
First, we construct an adjacency list \(g\) based on the given edges, where \(g[i]\) represents all neighbor nodes of node \(i\).
Then we traverse all nodes. For each node, we use DFS to traverse all its adjacent nodes and mark them as visited until all its adjacent nodes have been visited. In this way, we have found a connected component, and the answer is incremented by one. Then we continue to traverse the next unvisited node until all nodes have been visited.
The time complexity is \(O(n + m)\), and the space complexity is \(O(n + m)\), where \(n\) and \(m\) are the number of nodes and edges, respectively.
DFS needs the graph and a call stack. Union-find merges endpoints; a successful union decrements the component count from \(n\). No explicit adjacency list is required.
We can use a union-find set to maintain the connected components in the graph.
First, we initialize a union-find set, then traverse all the edges. For each edge \((a, b)\), we merge nodes \(a\) and \(b\) into the same connected component. If the merge is successful, it means that nodes \(a\) and \(b\) were not in the same connected component before, and the number of connected components decreases by one.
Finally, we return the number of connected components.
The time complexity is \(O(n + m \alpha(n))\), and the space complexity is \(O(n)\), where \(n\) and \(m\) are the number of nodes and edges, respectively, and \(\alpha(n)\) is the inverse of the Ackermann function, which can be regarded as a very small constant.
Solution 1 expands each component with an explicit stack. A queue expands the same component in breadth-first order, and each new search still increments the count. Time stays \(O(n + m)\).
We can also use BFS (Breadth-First Search) to count the number of connected components in the graph.
Similar to Solution 1, we first construct an adjacency list \(g\) based on the given edges. Then we traverse all nodes. For each node, if it has not been visited, we start BFS traversal from this node, marking all its adjacent nodes as visited, until all its adjacent nodes have been visited. In this way, we have found a connected component, and the answer is incremented by one.
After traversing all nodes, we get the number of connected components in the graph.
The time complexity is \(O(n + m)\), and the space complexity is \(O(n + m)\), where \(n\) and \(m\) are the number of nodes and edges, respectively.
The number of connected components is the number of searches that start at an unvisited node and cover its component. With \(n \le 2000\), one \(O(n + m)\) traversal is enough. Recursing along a chain uses a call depth equal to the node count and overflows Python once the chain reaches length \(1000\). Each new component is therefore expanded on a stack: a node is marked when it is pushed, and unvisited neighbors are pushed after it is popped. Marking on push keeps every node out of the stack more than once. Each new start increments the answer.
Build an adjacency list \(g\) from the given edges. \(g[i]\) holds every neighbor of node \(i\).
Scan the nodes. When node \(i\) is still unvisited, increment the answer, push \(i\), and mark it visited. While the stack is not empty, pop node \(u\) and push every neighbor that has not been visited, marking it at the same time. That search covers the component of \(i\). Continue with the next unvisited node until every node has been seen. The order of the neighbors does not change the number of components.
The time complexity is \(O(n + m)\), and the space complexity is \(O(n + m)\), where \(n\) and \(m\) are the number of nodes and edges, respectively.