Frontend Labs
⋯

20. Union-Find (Disjoint Set)

🧸 ELI5: Imagine everyone at a party starts in their own tiny friend group of 1. Whenever two people become friends, you merge their whole friend groups into one. Union-Find quickly answers: "are Alice and Bob in the same friend group?" and "merge these two groups together" — both almost instantly, even with millions of people.

🌍 Real life: Detecting if adding a road would create a cycle (used in building efficient road networks — Kruskal's algorithm). Grouping friends into "friend circles" on a social network. Detecting connected components in an image (like the "magic wand" tool in Photoshop grouping similar-colored pixels).

📝 Pseudocode:

function find(x):
    if parent[x] != x:
        parent[x] = find(parent[x])   // path compression: flatten the group
    return parent[x]

function union(x, y):
    rootX = find(x)
    rootY = find(y)
    if rootX != rootY:
        parent[rootX] = rootY          // merge the two groups

🐹 Go:

package main

import "fmt"

type UnionFind struct {
	parent []int
}

func NewUnionFind(n int) *UnionFind {
	parent := make([]int, n)
	for i := range parent {
		parent[i] = i // everyone starts as their own group leader
	}
	return &UnionFind{parent: parent}
}

func (uf *UnionFind) Find(x int) int {
	if uf.parent[x] != x {
		uf.parent[x] = uf.Find(uf.parent[x]) // flatten the chain on the way up
	}
	return uf.parent[x]
}

func (uf *UnionFind) Union(x, y int) {
	rootX, rootY := uf.Find(x), uf.Find(y)
	if rootX != rootY {
		uf.parent[rootX] = rootY // merge the friend groups
	}
}

func main() {
	people := NewUnionFind(5) // people 0,1,2,3,4
	people.Union(0, 1)        // 0 and 1 become friends
	people.Union(1, 2)        // now 0,1,2 are one group

	fmt.Println(people.Find(0) == people.Find(2)) // true, same group
	fmt.Println(people.Find(0) == people.Find(3)) // false, different group
}

🇨 C — the "path compression" line (uf->parent[x] = uf_find(uf, uf->parent[x])) is the entire trick: every lookup flattens the chain a little more, so future lookups get faster:

#include <stdio.h>

typedef struct { int parent[64]; } UnionFind;

void uf_init(UnionFind *uf, int n) {
    for (int i = 0; i < n; i++) uf->parent[i] = i; // everyone starts as their own group leader
}

int uf_find(UnionFind *uf, int x) {
    if (uf->parent[x] != x) uf->parent[x] = uf_find(uf, uf->parent[x]); // flatten on the way up
    return uf->parent[x];
}

void uf_union(UnionFind *uf, int x, int y) {
    int root_x = uf_find(uf, x), root_y = uf_find(uf, y);
    if (root_x != root_y) uf->parent[root_x] = root_y; // merge the friend groups
}

int main(void) {
    UnionFind people;
    uf_init(&people, 5); // people 0,1,2,3,4
    uf_union(&people, 0, 1); // 0 and 1 become friends
    uf_union(&people, 1, 2); // now 0,1,2 are one group

    printf("%d\n", uf_find(&people, 0) == uf_find(&people, 2)); // 1 (true), same group
    printf("%d\n", uf_find(&people, 0) == uf_find(&people, 3)); // 0 (false), different group
    return 0;
}

⏱️ Complexity: With path compression, find/union are nearly O(1) (technically O(α(n)), an incredibly slow-growing function — for all practical purposes, instant).


Generated from real-life-ds-algo-guide.md · 8 Oct 2026 16:36