0
p[0]
Given n nodes and a list of undirected edges, count how many connected components exist. Union-Find solves this in near-linear time: start with n components and decrement each time a successful merge joins two previously separate groups.
You have n nodes labeled 0 to n-1 and a list of undirected edges. Return the number of connected components — groups of nodes that can reach each other. For n=5 and edges [[0,1],[1,2],[3,4]], nodes 0–1–2 form one component and 3–4 form another, so the answer is 2.
parent[i] = i for every node; set components = n. Each node is its own root.parent[] until you reach a self-loop. Path halving flattens the tree as you go — nearly O(1) amortized.find on both endpoints. If roots differ, set parent[rootB] = rootA — merging the two trees — then components--.components.if (ra !== rb) before touching the counter.Both are optimal in big-O terms, but Union-Find is incremental: you can add edges one at a time and the component count stays correct — BFS must restart from scratch for each batch.
5 nodes, each its own component. parent[i] = i for all i. Components = 5.function countComponents(n: number, edges: number[][]): number {
const parent = Array.from({ length: n }, (_, i) => i);
function find(x: number): number {
while (parent[x] !== x) {
parent[x] = parent[parent[x]]; // path compression (halving)
x = parent[x];
}
return x;
}
function union(a: number, b: number): boolean {
const ra = find(a);
const rb = find(b);
if (ra === rb) return false; // already connected
parent[rb] = ra; // merge rb's tree under ra
return true;
}
let components = n;
for (const [a, b] of edges) {
if (union(a, b)) components--; // successful merge = one fewer component
}
return components;
}Array.from with the index initializer is the idiomatic one-liner.parent[x] until a self-loop gives the root. The line parent[x] = parent[parent[x]] skips one level each visit — no extra storage, nearly flat tree after a few operations.false so the caller knows not to decrement. Otherwise link rb's tree under ra and return true.n. Each successful union reduces the count by one. After all edges the counter is the answer.find(i) as the bucket key in a Map.n-1 edges, no cycle, and is fully connected — equivalently, Union-Find reports exactly 1 component and every edge is a successful merge.find(a) === find(b) before the union is the redundant one (it closes a cycle).| "number of connected components" | Union-Find, start at n and decrement |
| same-root check on edge | find(a) === find(b) → skip; else union + -- |
| "graph valid tree / no cycle" | Union-Find: every edge must be a successful merge |
| edges arrive online / incrementally | Union-Find (BFS must restart) |
const parent = Array.from({ length: n }, (_, i) => i);
function find(x: number): number {
while (parent[x] !== x) {
parent[x] = parent[parent[x]]; // path halving
x = parent[x];
}
return x;
}
let components = n;
for (const [a, b] of edges) {
const ra = find(a), rb = find(b);
if (ra !== rb) {
parent[rb] = ra;
components--;
}
}
return components;n=5, edges [[0,1],[1,2],[3,4]]. How many connected components?components = n and process 5 edges where 3 are successful unions and 2 are redundant, what is the final component count?