Pattern #56

Union Find / Disjoint Set Union

Important interview questions, thinking patterns, Path Compression, Union by Size, Undirected Cycle Detection, Kruskal's MST, and Go templates.

Must Solve

18 core questions — solve these first.

  1. 1.Number of Connected Components in an Undirected Graph
    medium
  2. 2.Redundant Connection
    medium
  3. 3.Number of Provinces
    medium
  4. 4.Graph Valid Tree
    medium
  5. 5.Accounts Merge
    medium
  6. 6.Most Stones Removed with Same Row or Column
    medium
  7. 7.Number of Operations to Make Network Connected
    medium
  8. 8.Satisfiability of Equality Equations
    medium
  9. 9.Smallest String With Swaps
    medium
  10. 10.Similar String Groups
    hard
  11. 11.Regions Cut By Slashes
    medium
  12. 12.Largest Component Size by Common Factor
    hard
  13. 13.Min Cost to Connect All Points
    medium
  14. 14.Kruskal's Minimum Spanning Tree
    medium
  15. 15.Number of Islands II
    hard
  16. 16.Remove Max Number of Edges to Keep Graph Fully Traversable
    hard
  17. 17.Checking Existence of Edge Length Limited Paths
    hard
  18. 18.Find Critical and Pseudo-Critical Edges in MST
    hard

Also Important

7 more questions worth practicing.

  1. 19.Sentence Similarity II
    medium
  2. 20.Connecting Cities With Minimum Cost
    medium
  3. 21.Earliest Moment When Everyone Becomes Friends
    medium
  4. 22.Minimize Malware Spread
    hard
  5. 23.Couples Holding Hands
    hard
  6. 24.Friend Requests with Restrictions
    hard
  7. 25.Dynamic Connectivity Problems
    hard

How to Think

  1. Need repeatedly ask: are these connected?Union Find (DSU)
  2. Need merge groups?Union(u, v)
  3. Need representative of a group?Find(u)
  4. Adding edge creates cycle?Find(u) == Find(v) before edge addition
  5. Need Minimum Spanning Tree?Kruskal's Algorithm + DSU

Go DSU Struct Code Template

Complete DSU struct with Path Compression & Union by Size in Go

type DSU struct {
    parent []int
    size   []int
}

func NewDSU(n int) *DSU {
    p := make([]int, n)
    s := make([]int, n)
    for i := 0; i < n; i++ {
        p[i] = i
        s[i] = 1
    }
    return &DSU{parent: p, size: s}
}

// Find with Path Compression: O(alpha(N)) amortized (~O(1))
func (d *DSU) Find(x int) int {
    if d.parent[x] != x {
        d.parent[x] = d.Find(d.parent[x]) // Flatten tree!
    }
    return d.parent[x]
}

// Union by Size: O(alpha(N)) amortized (~O(1))
func (d *DSU) Union(a, b int) bool {
    rootA := d.Find(a)
    rootB := d.Find(b)

    if rootA == rootB {
        return false // Already connected! Same root -> Cycle detected!
    }

    // Attach smaller tree under larger tree
    if d.size[rootA] < d.size[rootB] {
        rootA, rootB = rootB, rootA
    }
    d.parent[rootB] = rootA
    d.size[rootA] += d.size[rootB]
    return true // Successful merge!
}

👉 Total Time: Amortized O(α(N)) ≈ O(1) per op | Space: O(N) parent & size arrays

Path Compression & Union by Size

1. Path Compression: During Find(x), setting parent[x] = Find(parent[x]) re-links all visited nodes directly to the root, flattening tall tree chains to height 1!

2. Union by Size: When merging roots rootA and rootB, attach the smaller tree under the larger tree (d.parent[rootB] = rootA) to keep trees shallow.

3. Component Counter: Initialize components = n. Every time Union(u, v) returns true (successful merge), decrement components--.

Undirected Cycle Detection & Kruskal MST

1. Cycle Detection: If Find(u) == Find(v) before adding undirected edge u - v, nodes u and v are ALREADY connected! Adding edge u - v creates a cycle (used in Redundant Connection).

2. Kruskal's Minimum Spanning Tree: Sort all edges by weight $\to$ Iterate edges and call Union(u, v). If Union returns true, add edge weight to MST total; if false, skip (would create a cycle!).

Visual Memory Rule
DSU / Union Find → Efficiently track and merge connected components
Find(x)          → parent[x] = Find(parent[x]) (Path Compression -> O(1))
Union(a, b)      → Merge smaller root under larger root (Union by Size)
Find(u) == Find(v) -> Nodes already connected! Undirected Cycle Detected!
Kruskal MST      → Sort edges by weight -> Add edge if Union(u, v) succeeds
Accounts Merge   → Shared email identifier -> Union account IDs

💡 Golden Rule: "Every connected group has one representative root; Find tells you the group, and Union merges two different groups into one."

Common Interview Mistakes

1. Forgetting to Find Roots Before Union

Wrong: parent[b] = a. Correct: rootA = Find(a); rootB = Find(b); parent[rootB] = rootA.

2. Comparing Node Parents Instead of Roots

Wrong: parent[a] == parent[b]. Parents may differ while roots match! Correct: Find(a) == Find(b).

3. Decreasing Component Count on Failed Union

Only decrement components-- when Union(u, v) successfully merges two DIFFERENT roots!

4. Using DSU for Directed Cycle Detection

Basic DSU is designed for UNDIRECTED connectivity! Use Kahn's Algorithm or 3-State DFS for DIRECTED cycle detection.

Interview Rules

  1. 1. Repeated connectivity queries? → Union Find / DSU
  2. 2. Find representative root?parent[x] = Find(parent[x]) (Path Compression)
  3. 3. Merge groups? → Attach smaller root under larger root (Union by Size)
  4. 4. Same root check?Find(u) == Find(v)
  5. 5. Undirected cycle check? → If Find(u) == Find(v) before adding edge $\implies$ Cycle!
  6. 6. Redundant Connection? → Return edge where Union(u, v) == false
  7. 7. Minimum Spanning Tree? → Sort edges by weight + Kruskal DSU
  8. 8. Overall Complexity → Time: O(α(N)) ≈ O(1) per operation | Space: O(N)

Small Rules

  1. Rule 1: Every connected component in DSU is identified by a single root node.
  2. Rule 2: Path compression flattens lookup paths so future queries take O(1) time.
  3. Rule 3: Union by size prevents deep tree structures when joining sets.
  4. Rule 4: Two nodes share an undirected cycle if their roots match before connecting them.
  5. Rule 5:Kruskal's MST processes edges in ascending weight order using DSU.

Production Thinking

Network Server Connectivity QueriesInstantly checking if two data center servers share a connected link

Identity Resolution Record ClusteringMerging duplicate user profiles sharing emails, phone numbers, or device IDs

Infrastructure Region GroupingGrouping cloud compute nodes into connected availability zones

Image Connected Component LabelingMerging adjacent image pixels into visual object clusters

Remember This

Union Find / DSU → Track & Merge Disjoint Groups

Find(x)          → parent[x] = Find(parent[x]) (Path Compression)
Union(a, b)      → Merge smaller root under larger root (Union by Size)
Same Root        → Find(a) == Find(b) (Connected!)
Cycle Check      → Find(u) == Find(v) before adding edge
Components       → Start N, successful Union: --
Kruskal MST      → Sort edges by weight + Union Find
Time             → O(alpha(N)) per operation ≈ O(1)

💡 Golden Rule: "Every connected group has one representative root; Find tells you the group, and Union merges two different groups into one."