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.Number of ProvincesAdjacency Matrix: outer loop 0..n-1; if !visited[i] -> components++ & DFS(i)medium
- 2.Number of Connected Components in an Undirected GraphBuild Adjacency List; outer loop unvisited nodes to count DFS triggersmedium
- 3.Number of IslandsGrid Components: scan cells; if grid[r][c] == "1" -> islands++ & Grid DFS/BFSmedium
- 4.Max Area of IslandGrid DFS returning 1 + sum(dfs(neighbor)) per component; track max areamedium
- 5.Graph Valid TreeConnected Components == 1 AND edges == n - 1medium
- 6.Accounts MergeEmail Graph / Union Find: Emails sharing an account form graph edgesmedium
- 7.Friend Circles / ProvincesAdjacency Matrix DFS component countmedium
- 8.Count Sub IslandsGrid DFS: island in grid2 is sub-island if all its cells are land in grid1medium
- 9.Closed IslandsBoundary Grid DFS: flood fill border land, count remaining closed islandsmedium
- 10.Number of EnclavesBoundary Grid DFS: flood fill boundary land, count remaining unreached land cellsmedium
- 11.Find if Path Exists in GraphVerify if source and destination belong to same connected componenteasy
- 12.Redundant ConnectionUnion Find: find first edge where find(u) == find(v)medium
- 13.Smallest String With SwapsUnion Find / Component DFS: group indices into components & sort characters inside each componentmedium
- 14.Similar String GroupsConnect similar strings with edges + Connected Components DFShard
- 15.Most Stones Removed with Same Row or ColumnConnect stones in same row/col; max stones removed = totalStones - numComponentsmedium
- 16.Number of Operations to Make Network ConnectedIf edges < n - 1 return -1; operations needed = numComponents - 1medium
Also Important
8 more questions worth practicing.
- 17.Surrounded RegionsBoundary Grid DFS: flood fill border "O"s, capture remaining "O"s to "X"medium
- 18.Largest Component Size by Common FactorUnion Find connecting numbers to their prime factorshard
- 19.Regions Cut By SlashesSplit each grid cell into 4 sub-triangles + Union Find connectivitymedium
- 20.Number of Distinct IslandsGrid DFS + path shape serialization (e.g. "U", "D", "L", "R", "B") stored in Hash Setmedium
- 21.Count Unreachable Pairs of NodesCompute component sizes; answer = sum(size * (n - size)) / 2medium
- 22.Satisfiability of Equality EquationsUnion Find: union equal variables "a==b", verify non-equals "a!=b" have different rootsmedium
- 23.Sentence Similarity IIMap-based Graph / Union Find checking word pair connectivitymedium
- 24.Accounts / User Identity GroupingIdentity Deduplication Graph: merge user profiles sharing phone/emailmedium
How to Think
- Need count separate groups?Connected Components (Outer loop DFS/BFS)
- Need size of each group?DFS/BFS + increment size++ during traversal
- Need merge groups dynamically as edges arrive?Union Find (Disjoint Set Union)
- 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.
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
A single DFS call from node 0 only visits node 0's component! You MUST run an outer loop over all n nodes.
Nodes with zero edges do NOT appear in edge lists, but are still valid components of size 1. Allocate using n.
Connected Components visited marking is PERMANENT. Do NOT unmark or backtrack visited[u] = false.
Basic Connected Components assumes undirected reachability. Directed graphs require Strongly Connected Component algorithms (Kosaraju/Tarjan).
Interview Rules
- 1. Count separate groups? → Connected Components (Outer loop DFS/BFS)
- 2. Group size required? → Increment
size++during DFS/BFS traversal - 3. Grid Islands / Provinces? → 2D Cell Grid Connected Components
- 4. Dynamic edge merging? → Union Find (Disjoint Set Union)
- 5. Isolated nodes? → Count as component of size 1 (outer loop covers all
nnodes) - 6. Graph Valid Tree? → Components == 1 AND edges == n - 1
- 7. Most Stones Removed? → Answer =
totalStones - numComponents - 8. Overall Complexity → Traversal Time:
O(V + E)| Union Find Time:O(E · α(V))
Small Rules
- Rule 1: New unvisited node in outer loop = new connected component.
- Rule 2: A single DFS/BFS call marks the entire component.
- Rule 3: Total component count = total DFS/BFS outer loop triggers.
- Rule 4: Static graphs use DFS/BFS; dynamic edge streams use Union Find.
- Rule 5: Grid cells with 4-directional offsets form matrix components.
Production Thinking
User Identity Deduplication → Merging duplicate accounts sharing email/phone into single user profiles
Network Isolated Segments → Identifying disconnected computer subnets & network security zones
Social Community Detection → Grouping mutually reachable users into distinct online communities
Microservice Failure Isolation → Determining 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."