← Daily Logs
LeetCode 323

Number of Connected Components in an Undirected Graph

MediumJul 9, 2026graphdfsconnected-componentsedge-list

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

js
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)

All entries