Pattern #51

Connected Components

Important interview questions, thinking patterns, Outer Loop Discovery pattern, Component Size calculations, DFS/BFS vs Union Find tradeoffs, and Go code templates.

Must Solve

16 core questions — solve these first.

  1. 1.Number of Provinces
    medium
  2. 2.Number of Connected Components in an Undirected Graph
    medium
  3. 3.Number of Islands
    medium
  4. 4.Max Area of Island
    medium
  5. 5.Graph Valid Tree
    medium
  6. 6.Accounts Merge
    medium
  7. 7.Friend Circles / Provinces
    medium
  8. 8.Count Sub Islands
    medium
  9. 9.Closed Islands
    medium
  10. 10.Number of Enclaves
    medium
  11. 11.Find if Path Exists in Graph
    easy
  12. 12.Redundant Connection
    medium
  13. 13.Smallest String With Swaps
    medium
  14. 14.Similar String Groups
    hard
  15. 15.Most Stones Removed with Same Row or Column
    medium
  16. 16.Number of Operations to Make Network Connected
    medium

Also Important

8 more questions worth practicing.

  1. 17.Surrounded Regions
    medium
  2. 18.Largest Component Size by Common Factor
    hard
  3. 19.Regions Cut By Slashes
    medium
  4. 20.Number of Distinct Islands
    medium
  5. 21.Count Unreachable Pairs of Nodes
    medium
  6. 22.Satisfiability of Equality Equations
    medium
  7. 23.Sentence Similarity II
    medium
  8. 24.Accounts / User Identity Grouping
    medium

How to Think

  1. Need count separate groups?Connected Components (Outer loop DFS/BFS)
  2. Need size of each group?DFS/BFS + increment size++ during traversal
  3. Need merge groups dynamically as edges arrive?Union Find (Disjoint Set Union)
  4. Grid contains separate land regions?Connected Components on Grid (Islands)

Go Connected Components Templates

DFS Component Counter & Union Find Disjoint Set in Go

// 1. Connected Components DFS: O(V + E) Time
func countComponents(n int, edges [][]int) int {
    adj := make([][]int, n)
    for _, e := range edges {
        u, v := e[0], e[1]
        adj[u] = append(adj[u], v)
        adj[v] = append(adj[v], u)
    }

    visited := make([]bool, n)
    var dfs func(u int)
    dfs = func(u int) {
        visited[u] = true
        for _, neighbor := range adj[u] {
            if !visited[neighbor] {
                dfs(neighbor)
            }
        }
    }

    components := 0
    // Outer loop over ALL n nodes (includes isolated nodes of size 1!)
    for i := 0; i < n; i++ {
        if !visited[i] {
            components++
            dfs(i)
        }
    }
    return components
}

// 2. Union Find (Disjoint Set Union): O(E · α(V)) Time
type UnionFind struct {
    parent []int
    rank   []int
    count  int
}

func NewUnionFind(n int) *UnionFind {
    p := make([]int, n)
    r := make([]int, n)
    for i := 0; i < n; i++ { p[i] = i }
    return &UnionFind{parent: p, rank: r, count: n}
}

func (uf *UnionFind) Find(i int) int {
    if uf.parent[i] != i {
        uf.parent[i] = uf.Find(uf.parent[i]) // Path Compression!
    }
    return uf.parent[i]
}

func (uf *UnionFind) Union(i, j int) bool {
    rootI, rootJ := uf.Find(i), uf.Find(j)
    if rootI == rootJ { return false } // Already in same component!
    
    // Union by Rank
    if uf.rank[rootI] < uf.rank[rootJ] {
        rootI, rootJ = rootJ, rootI
    }
    uf.parent[rootJ] = rootI
    if uf.rank[rootI] == uf.rank[rootJ] {
        uf.rank[rootI]++
    }
    uf.count--
    return true
}

👉 DFS Time: O(V + E) | Union Find Time: O(E · α(V)) ≈ O(E)

The Golden Outer Loop Discovery Pattern

1. Golden Observation: One DFS or BFS call started from an unvisited node floods and marks the entire connected component.

2. Outer Loop Scanning: Loop through all nodes 0..n-1 (or all grid cells). Every time you encounter an unvisited node, increment components++ and trigger a DFS/BFS.

3. Isolated Nodes: A node with 0 edges is still a valid component of size 1! The outer loop ensures isolated nodes are counted correctly.

DFS/BFS vs Union Find Tradeoffs

1. Static Graph (DFS/BFS): Use DFS or BFS when the graph is already built up-front and you need to traverse component members, calculate exact component sizes, or work on 2D grids (O(V + E) time).

2. Dynamic Edges (Union Find): Use Union Find (Disjoint Set) when edges arrive dynamically over time, when detecting cycles on edge insertion (Redundant Connection), or when merging equivalence classes (Accounts Merge, Satisfiability of Equality Equations) in near-constant O(α(V)) time per operation.

Visual Memory Rule
Connected Components → Separate reachable groups in an undirected graph
Outer Loop Trigger   → for node 0..n-1: if !visited[i] -> components++ & DFS(i)
Component Size       → Count size++ during traversal (Max Area, Unreachable Pairs)
Static Graph         → DFS / BFS (O(V + E) Time)
Dynamic Merges       → Union Find (O(E · α(V)) Time)

💡 Golden Rule: "Every time you find a node that no previous traversal could reach, you have found a new connected component."

Common Interview Mistakes

1. Starting Traversal Only Once

A single DFS call from node 0 only visits node 0's component! You MUST run an outer loop over all n nodes.

2. Forgetting Isolated Nodes

Nodes with zero edges do NOT appear in edge lists, but are still valid components of size 1. Allocate using n.

3. Unmarking Visited Array by Mistake

Connected Components visited marking is PERMANENT. Do NOT unmark or backtrack visited[u] = false.

4. Confusing Directed & Undirected Components

Basic Connected Components assumes undirected reachability. Directed graphs require Strongly Connected Component algorithms (Kosaraju/Tarjan).

Interview Rules

  1. 1. Count separate groups? → Connected Components (Outer loop DFS/BFS)
  2. 2. Group size required? → Increment size++ during DFS/BFS traversal
  3. 3. Grid Islands / Provinces? → 2D Cell Grid Connected Components
  4. 4. Dynamic edge merging? → Union Find (Disjoint Set Union)
  5. 5. Isolated nodes? → Count as component of size 1 (outer loop covers all n nodes)
  6. 6. Graph Valid Tree? → Components == 1 AND edges == n - 1
  7. 7. Most Stones Removed? → Answer = totalStones - numComponents
  8. 8. Overall Complexity → Traversal Time: O(V + E) | Union Find Time: O(E · α(V))

Small Rules

  1. Rule 1: New unvisited node in outer loop = new connected component.
  2. Rule 2: A single DFS/BFS call marks the entire component.
  3. Rule 3: Total component count = total DFS/BFS outer loop triggers.
  4. Rule 4: Static graphs use DFS/BFS; dynamic edge streams use Union Find.
  5. Rule 5: Grid cells with 4-directional offsets form matrix components.

Production Thinking

User Identity DeduplicationMerging duplicate accounts sharing email/phone into single user profiles

Network Isolated SegmentsIdentifying disconnected computer subnets & network security zones

Social Community DetectionGrouping mutually reachable users into distinct online communities

Microservice Failure IsolationDetermining blast radius boundaries across disconnected service dependency clusters

Remember This

Connected Components → Separate reachable groups
Loop all nodes
Unvisited?           → components++
Then                 → DFS/BFS marks whole group
Grid islands         → Matrix components
Component size       → Count nodes during traversal
Dynamic merging      → Union Find
Isolated node        → Component of size 1
Time                 → O(V + E)

💡 Golden Rule: "Every time you find a node that no previous traversal could reach, you have found a new connected component."