</> MAANG.io
coding interview Β· 101

Foundations

Master coding interviews with comprehensive coverage of data structures, algorithms, and problem-solving techniques. Progress from fundamentals to advanced topics with expertly curated content.

0/255 solved 0% complete

Union Find (Disjoint Set Union)

What is this?

Imagine a room full of strangers who keep discovering they're long-lost cousins. Every time two of them learn they're related, their whole families merge into one bigger family. Union-Find is exactly that bookkeeping: it tracks who belongs to which group, lets you merge two groups instantly, and lets you check whether two people are already in the same group. It is the fastest way to answer "are these two things connected?" as connections keep arriving.

flowchart TD A["Start each item in its own group"] --> B["Read a connection between two items"] B --> C["Find the leader of each one"] C --> D["Same leader means already connected"] C --> E["Different leaders means merge the groups"] E --> B

πŸ’‘ Fun fact: the near-instant speed of Union-Find comes with a famously weird footnote. Its true running time involves the inverse Ackermann function, which grows so unimaginably slowly that for any number of items that could ever physically exist, its value is at most 4 β€” making each operation "effectively constant time."

πŸ”“ The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: Union-Find maintains a partition of n elements into disjoint groups under two operations β€” union(a, b) merges the groups of a and b, and find(x) returns a canonical representative ("root") of x's group. Two elements are connected iff they share a root. With path compression and union by rank/size, both operations run in amortized near-O(1) β€” and a single integer "component count" answers "how many groups are left?" for free.


1. What Union-Find (Disjoint Set Union) really means

Each element points at a parent; follow parents until you hit a node that points at itself β€” that's the group's root. A group is therefore a little rooted tree, and the whole structure is a forest of such trees.

Union-Find forest of two rooted trees with parent array [0,0,1,3,3]: group {0,1,2} has root 0 and group {3,4} has root 3, so 2 and 4 are not connected

find(x) walks x up to its root. union(a, b) finds both roots and, if different, points one root at the other β€” merging two trees into one.

Two optimizations keep the trees flat (and the cost tiny):

  • Path compression (inside find): after locating the root, re-point every node on the path directly at the root, so future finds are near-instant.
    Path compression on find(2): the chain 0-1-2 flattens so both 1 and 2 point straight at root 0
  • Union by rank/size: always hang the shorter/smaller tree under the taller/larger root, so trees never grow unnecessarily tall.

2. Where you'll actually meet this

Disjoint-set thinking is the backbone of "is everything connected, and into how many pieces?" at scale:

  • Distributed systems. Connected-components over a node/link graph, network-partition detection (did the cluster split into β‰₯2 groups?), and Kruskal's MST for building a minimum-cost spanning overlay all sit directly on Union-Find. Component count == 1 means "fully reachable."
  • Networking. Loop detection in switch fabrics and merging IP/subnet equivalence classes β€” a new link that joins two already-connected nodes is a redundant loop.
  • Image / data processing. Connected-component labelling (flood-fill regions), clustering by transitive "same-as" relations (entity resolution, dedup).
  • Build / package systems. Grouping mutually dependent modules; detecting that adding a dependency would merge cycles.
  • Finance / compliance. Grouping accounts by transitive ownership/relationship; flagging that two "separate" entities are actually one connected group.

If a problem says connected, components, merge, same group, cycle on an undirected/streamed edge list β€” reach for Union-Find before BFS/DFS.


3. The Union-Find toolkit

  1. Parent array. parent = list(range(n)) β€” each element starts as its own root (n singleton groups).
  2. find with path compression. Climb to the root, then flatten the path. The one-liner recursive form:
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])   # compress on the way up
        return self.parent[x]
    
  3. union by rank (or size). Merge roots, shorter under taller (rank) or smaller under bigger (size); return False if already merged β€” that "already same root" is your cycle / redundant-edge signal.
    def union(self, x, y):
        rx, ry = self.find(x), self.find(y)
        if rx == ry:
            return False                       # same group β‡’ cycle / redundant
        if self.rank[rx] < self.rank[ry]:
            rx, ry = ry, rx
        self.parent[ry] = rx
        if self.rank[rx] == self.rank[ry]:
            self.rank[rx] += 1
        self.components -= 1                    # one fewer group
        return True
    
  4. The near-O(1) result. Path compression and union by rank together give an amortized cost of O(Ξ±(n)) per operation, where Ξ± is the inverse Ackermann function β€” Ξ±(n) ≀ 4 for any n you will ever see. Effectively constant.
  5. The "count components" trick. Start components = n; decrement on every successful union. At the end, components is the number of disjoint groups β€” == 1 means fully connected, which is the heart of both problems in this chapter.

4. A 30-second worked example

Five elements 0..4; apply union(0,1), union(2,3), union(1,2), then find(3) and check connected(0,3). Watch components fall from 5 toward the answer.

Worked example: three unions drop components from 5 to 2, forming tree {0,1,2,3} rooted at 0 plus lone node 4; find(3) gives root 0 and connected(0,3) is TRUE

Three unions, one find, one connectivity query β€” all amortized near-constant, and the running components counter told us the graph is not fully connected (node 4 is alone).


5. Problems in this chapter

β–Ά Graph Valid Tree

Decide whether an undirected edge list forms a tree (think: a loop-free, fully-connected fiber backbone across racks). Lead with the counting law β€” len(edges) != n-1 β†’ instant false β€” then union each edge, rejecting any edge whose endpoints already share a root (that's a cycle). Survive all n-1 unions β‡’ one component β‡’ tree.
Pattern: cycle-detection-on-union + n-1/connectivity. Target: O(n + mΒ·Ξ±(n)), O(n).

β–Ά Validate Binary Tree Nodes

Verify that directed parentβ†’child links form exactly one valid binary tree (think: a task-orchestration plan with one entry point, no double-owned job, no cyclic trigger chain). Guard ≀ 1 parent per node with a flag, reject the cycle on a same-root union, and require components == 1 at the end (connected + single-root).
Pattern: indegree guard + Union-Find connectivity/cycle. Target: O(nΒ·Ξ±(n)), O(n).

Both reduce a "is this a valid tree?" question to the same two Union-Find signals: a same-root union means a cycle, and a final component count of 1 means connected.


6. Common pitfalls 🚫

  • Skipping an optimization. Path compression without union by rank (or vice versa) degrades toward O(log n) or even O(n) chains. Use both for the inverse-Ackermann guarantee.
  • Forgetting the n-1 / component check. Cycle-free is not the same as connected. A forest of two clean trees has no cycle but isn't one tree β€” you must also confirm a single component.
  • Mutating parent outside union/find. Hand-editing parents breaks the rank invariant and corrupts future merges.
  • Treating "already same root" as benign. That return-False is load-bearing β€” it is your cycle / redundant-edge detector; ignoring it accepts invalid graphs.
  • Assuming direction. Union-Find is inherently undirected; for directed problems (like binary-tree validation) layer an indegree/parent guard on top to capture the direction-specific rules.

7. Key takeaways

  1. Union-Find = disjoint groups under find + union, with two elements "connected" iff they share a root β€” the go-to for connectivity, components, and cycle-on-edge questions.
  2. Path compression + union by rank/size make operations amortized O(Ξ±(n)) β€” effectively constant; always apply both.
  3. A same-root union is a cycle; a component count of 1 is full connectivity β€” the two signals that solve every problem in this chapter.
  4. components = n, decrement on each successful union gives you "how many groups?" for free β€” the cleanest connectivity proof there is.
  5. Reach for it over BFS/DFS when edges stream in, when you must answer "did this edge close a loop?", or when you only need group membership β€” Union-Find needs no adjacency list and no recursion over the graph.

Graph Valid Tree

Core idea: A graph on n nodes is a tree if and only if it has exactly n-1 edges and is fully connected β€” those two facts together force "no cycles" for free. Union-Find adds the edges one at a time, refusing any edge whose two endpoints are already in the same group (that edge would close a cycle); if all n-1 edges merge two distinct groups, you end with one group and a valid tree.


1. The problem, in a fresh setting

You run a fleet of n data-center racks, labelled 0 … n-1, and a network team hands you a list of fiber links they've laid, each link [a, b] joining rack a to rack b (bidirectional). You want the cabling to form a clean backbone tree: every rack reachable from every other rack (no isolated island), and no redundant loop (a loop wastes fiber and creates a routing ambiguity). Validate the wiring plan.

Formally: given an integer n and a list of undirected edges, return true if the edges form a valid tree β€” a graph that is connected and acyclic β€” and false otherwise.

n edges Valid tree? Why
5 [[0,1],[0,2],[0,3],[1,4]] true 4 edges, all reachable, no loop
5 [[0,1],[1,2],[2,3],[1,3],[1,4]] false 5 edges; loop 1-2-3-1
4 [[0,1],[2,3]] false 2 islands {0,1} and {2,3}
1 [] true a single rack is a trivial tree

Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account β€” no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.