Number of Connected Components in an Undirected Graph
Same stack DFS as `connectedComponentsCount`, but building the adjacency list as a plain object keyed by `for...in` fails silently: string keys and numeric edge values end up as distinct entries in the same `visited` set, so components get double-counted. Swapping the object for an array indexed by node number fixes it end to end.
The problem
Implement `countComponents(n, edges)` where nodes are labelled `0` to `n - 1` and `edges[i] = [a, b]` describes an undirected edge. Return how many connected components the graph has.
Unlike `connectedComponentsCount`, there's no adjacency list handed in and no guarantee every node appears in `edges` — a node with no edges at all is still its own component and has to be counted.
The approach
Building the adjacency list as a plain object keyed by `for...in` looks fine until it fails silently: object keys iterate as strings (`"0"`, `"1"`, ...) while the neighbour values pushed from `edges` stay numeric, so `visited` ends up holding both `"0"` and `0` as distinct entries. A node visited once as a string and once as a number gets explored twice, and components get double-counted.
The fix is to never let node identity leave numeric type: build `graph` as an array of length `n` (`Array.from({ length: n }, () => [])`) instead of an object, and drive the outer loop with `for (let node = 0; node < n; node++)` instead of `for...in`. Every index, edge endpoint, and `visited` entry now stays a number end to end.
Traversal itself is unchanged from `connectedComponentsCount` — the same `exploreGraph` stack DFS, the same shared `visited` set threaded across calls, the same 0/1 return repurposed as a running sum. Seeding `graph` with all `n` indices up front (rather than only creating keys for nodes edges mention) is also what makes isolated nodes count as size-one components instead of getting silently skipped.
The solution
class Solution {
/**
* @param {number} n
* @param {number[][]} edges
* @return {number}
*/
countComponents(n, edges) {
const graph = Array.from({ length: n }, () => []);
for (const [e1, e2] of edges) {
graph[e1].push(e2);
graph[e2].push(e1);
}
const visited = new Set();
let noOfComponents = 0;
for (let node = 0; node < n; node++) {
noOfComponents += this.exploreGraph(graph, node, visited);
}
return noOfComponents;
}
exploreGraph(graph, source, visited) {
if (visited.has(source)) return 0;
visited.add(source);
const stack = [source];
while (stack.length) {
const node = stack.pop();
for (const neighbour of graph[node]) {
if (!visited.has(neighbour)) {
visited.add(neighbour);
stack.push(neighbour);
}
}
}
return 1;
}
}Time O(V + E)Space O(V)