The Full Toolkit

Six lessons. Six building blocks. You've seen each piece in isolation — initialization, find chains, path compression, union by rank, cycle detection, component counting. Each one solved a specific problem. Each one had a specific moment where you felt the need before you got the tool.

Now: can you deploy them together?

Below is a five-part assessment that tests across the entire module. You'll match real LeetCode problems to the right technique, assemble the complete UnionFind class from blanks, spot bugs that produce silent wrong answers, recognize when UF is the wrong tool entirely, and rapid-fire through the core facts.

This is not a teaching screen. There are no new concepts. Everything you need, you've already learned. The question is whether you can retrieve it under pressure and apply it to unfamiliar problem statements.

Synthesis lab

Pattern Spotter
Question 1/5

LC 684 — Redundant Connection

A tree with one extra edge. Find the edge that, when removed, leaves a valid tree.

Which UF technique does this problem need?

Your Union-Find Toolkit

Here's the complete template — the same code you just assembled, with every optimization in place:

1
class UnionFind {
2
  parent: number[];
3
  rank: number[];
4
  components: number;
5
6
  constructor(n: number) {
7
    this.parent = Array.from({length: n}, (_, i) => i);
8
    this.rank = new Array(n).fill(0);
9
    this.components = n;
10
  }
11
12
  find(x: number): number {
13
    if (this.parent[x] !== x) {
14
      this.parent[x] = this.find(this.parent[x]);
15
    }
16
    return this.parent[x];
17
  }
18
19
  union(a: number, b: number): boolean {
20
    const rootA = this.find(a);
21
    const rootB = this.find(b);
22
    if (rootA === rootB) return false;
23
    if (this.rank[rootA] < this.rank[rootB]) {
24
      this.parent[rootA] = rootB;
25
    } else if (this.rank[rootA] > this.rank[rootB]) {
26
      this.parent[rootB] = rootA;
27
    } else {
28
      this.parent[rootB] = rootA;
29
      this.rank[rootA]++;
30
    }
31
    this.components--;
32
    return true;
33
  }
34
35
  connected(a: number, b: number): boolean {
36
    return this.find(a) === this.find(b);
37
  }
38
}

The trigger recognition checklist — when you see these patterns in a problem, reach for Union-Find:

  • "Are X and Y connected?" — Direct connected() call. LC 547, 990.
  • "Find the redundant edge" — Process edges in order; find(u) === find(v) before union = cycle. LC 684.
  • "How many connected components?"components = n - successful merges. LC 323, 200, 547.
  • "Group elements by equivalence" — Union equivalent pairs, then collect by root. LC 721, 990, 952.
  • "Minimum spanning tree" — Kruskal's algorithm: sort edges by weight, union greedily, skip cycles. LC 1135, 1584.

When NOT to use Union-Find:

  • Shortest paths (need Dijkstra/BFS, not just connectivity)
  • Edge deletion (UF is union-only, no split)
  • Directed reachability (UF is symmetric)
  • Enumerating reachable nodes (BFS/DFS is more natural)
“Find if adding this edge creates a cycle”
Dynamic connectivity?YES
Undirected?YES
Need components only?YES
✓ Union-Find
1/5

Six lessons built one data structure from scratch. You started with a buggy constructor and ended with a tool that solves connectivity, cycle detection, and component counting — all through the same parent-pointer forest.

Tap a technique to see its summary