2Brute2Best
🔥0

Union-Find (template)

Easy·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelUnion-Find (DSU), the base template
Complexity:O(N · Q)→O(Q · α(N))
Key Insight

Each group is a tree of parent pointers, and the root of the tree names the group. connected(x, y) compares find(x) with find(y). union(x, y) links one root under the other, so a union changes exactly one pointer and no node is renamed.

Core Validity Invariant

Two nodes are in one group exactly when they have the same root, so find(x) == find(y) is the whole connected check.

01

Problem

You have n nodes numbered 0 to n − 1. At the start, every node is alone in its own group.

You get a list of queries. Each query is [type, x, y]:

  • type 1 is a union: join the group of x with the group of y.
  • type 2 asks are x and y connected?: say whether x and y are in the same group right now. Record true or false.

Return the answers to the type-2 queries, in the order they appear.

Input

n, the number of nodes, and queries, a list of [type, x, y] triples.

Output

A list of booleans (vector<bool>): one answer for each type-2 query, in the same order.

Example 1
Input: n = 5, queries = [[1,0,1], [2,0,1], [2,1,2], [1,1,2], [2,0,2]]
Output: [true, false, true]

After joining 0 and 1, the nodes 0 and 1 are connected, but 2 is still alone. After joining 1 and 2, the group is {0, 1, 2}, so 0 and 2 are connected.

Example 2
Input: n = 4, queries = [[1,0,1], [1,2,3], [2,0,3], [1,1,3], [2,0,2]]
Output: [false, true]

The groups are {0, 1} and {2, 3} when we ask about 0 and 3, so the answer is no. Joining 1 and 3 merges all four nodes, so 0 and 2 are connected.

Constraints
  • • Every node number x and y is in the range 0 to n − 1.
  • • The type is 1 (union) or 2 (connected?).
  • • A node is always connected to itself.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    A union changes which nodes share a group. What stays the same for every other group?

  2. 2.

    How could I answer "are x and y connected?" without looking at every node in the group?

  3. 3.

    If every group had one name, what would a union have to rename?

  4. 4.

    If each node stores only one parent, how do I find the group's name from any node in it?

  5. 5.

    When I join two groups, do I attach the two nodes from the query, or something else?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceOne label per node; a union renames every node of one groupO(N · Q)O(N)
OptimalParent pointers; find each root, link roots (smaller under bigger)O(Q · α(N))O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “groups that merge over time”Items join groups as the queries arrive → keep groups with one root each.
  • “type 1: join x and y”A union: merge two groups, and never split them.
  • “type 2: are x and y connected?”Compare the roots of the two items; no search over the graph is needed.
Items that merge over time+union(x, y) joins two groups+connected(x, y) means same root→Union-Find (DSU), the base template

This is the base template of the whole pattern. Every later question keeps the same two operations, find and unite, and only changes what an item stands for or when two items are united. Here the nodes are plain numbers and the answers come straight from the roots, with nothing else to count.

10

Don't make this mistake

Linking the two nodes instead of their roots
✗ void unite(int a, int b) { parent[b] = a; }
✓ bool unite(int a, int b) { a = find(a); b = find(b); if (a == b) return false; parent[b] = a; return true; }

Pattern-confusion trap: this looks like a union, but it only works on nodes that are already roots. On the dry run, after union(0, 1) and union(2, 3), node 3 hangs under 2. Then union(1, 3) with the wrong code sets parent[3] = 1. Node 3 leaves its group, so the group {2, 3} is split and connected(2, 3) returns false. With find first, the roots are 0 and 2, so parent[2] = 0 and all four nodes stay together.

Comparing one step up instead of the roots
✗ return parent[x] == parent[y];
✓ return find(x) == find(y);

parent[x] only points one level up. After union(1, 3) on the dry run, parent[3] = 2 and parent[2] = 0. They differ, yet 2 and 3 are in one group. Always compare the roots.

Hanging the first root under the second, with no size rule
✗ parent[rb] = ra; // always the same direction
✓ if (sz[ra] < sz[rb]) swap(ra, rb); parent[rb] = ra; sz[ra] += sz[rb];

Always hanging the same side under the other can build a chain 0 ← 1 ← 2 ← … ← n−1. Then find walks n steps every time. The size rule keeps trees shallow, so every find stays short.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Union-Find (DSU) translate into production architectures and low-level memory mechanics.