Graph
Connected Components
547. Number of Provinces
Initialize a DSU with n nodes. Iterate the upper triangle of the matrix -- when isConnected[row][col] == 1, union the two cities. Each successful union reduces the component count by 1. Return getCount().
- Time
- O(n^2 * α(n))
nis the number of cities.- Scanning the upper triangle of
isConnectedcostsO(n^2)iterations. - Each
unioncall does afindon both endpoints, which with path compression and union by rank isO(α(n))amortized. - Space
- O(3n)
parent,rank, andsizeare each their own length-narray insideDisjointSets- three separateO(n)allocations.
323. Number of Connected Components in an Undirected Graph
Initialize DSU with n nodes. Process every edge with union(a, b) -- each successful merge decreases the component count by 1. Return getCount() after all edges.
- Time
- O(n + E * a(n))
DisjointSets(n)initializesparent,rank, andsize, each of lengthn:O(n).- The
for a, b in edgesloop callsuniononce per edge; with union by rank and path compression,findruns in amortizedO(a(n))(inverse Ackermann, effectively constant), so allEunion calls costO(E * a(n)). nis the number of nodes andEis the number of edges.- Space
- O(n)
self.parent,self.rank, andself.sizeeach holdnentries.
1971. Find if Path Exists in Graph
Initialize DSU with n nodes. Union all edges. Two nodes are connected iff they share the same root -- return find(source) == find(destination).
- Time
- O(E · α(n))
n= number of nodes,E= number of edges. Thefor a, b in edgesloop callsuniononce per edge, each doing twofindcalls with path compression and union by rank - amortizedO(α(n))per call, givingO(E · α(n))overall.- The final
find(source) == find(destination)check is two more amortizedO(α(n))calls, dominated by the edge loop. - Space
- O(n)
DisjointSets.__init__allocatesparent,rank, andsize, each of lengthn.
1101. The Earliest Moment When Everyone Become Friends
Sort logs by timestamp. Initialize DSU with n nodes. Process each log entry -- union the two people. After each union, if getCount() == 1 all n people are in one component; return the current timestamp. If no such moment exists, return -1.
- Time
- O(L * α(n) + L log L)
Lis the number of log entries,nthe number of people. Processing all logs costsLcalls tounion, eachO(α(n))amortized (path compression + union by rank) -L * α(n). Sorting the logs by timestamp first costsO(L log L), the dominant term.- Space
- O(sort + n)
- The DSU's
parent,rank, andsizearrays are each sizedn, on top of the sort's own working memory. - Sorting algorithms are typically
O(log n)space (in-place, recursion stack only), but Python'slist.sort()is Timsort, which allocates up toO(n)auxiliary space in the worst case - that's whatsortstands for here.
1319. Number of Operations to Make Network Connected
If there are fewer than n-1 cables, it is impossible to connect all n computers regardless of how they are rearranged -- return -1. Otherwise, union every connection. Redundant edges (those that join two already-connected computers) are cables that can be unplugged and reused. After processing all connections, the DSU has some number of components k. Each component is already internally connected, so we need exactly k-1 cable moves to link them into one network.
- Time
- O(E * α(n))
Eis the number of connections. Eachunioncall costsO(α(n))amortized thanks to path compression and union by rank, and the loop makes oneunioncall per connection.- Space
- O(n)
- The DSU's
parent,rank, andsizearrays are each sizedn.
Cycle Detection
261. Graph Valid Tree
A valid tree has exactly n-1 edges and no cycles. Initialize DSU with n nodes. For each edge (a, b), call union(a, b) -- if it returns False, the edge creates a cycle so the graph is not a tree. After all edges, check getCount() == 1 to ensure full connectivity.
- Time
- O(E * a(n))
Eis the number of edges,nis the number of nodes.- Each edge triggers one
unioncall, which callsfind(with path compression) on both endpoints and unions by rank - amortizedO(a(n))per call, whereais the inverse Ackermann function. - A cycle triggers an early
return False, but the amortized bound already reflects the cheapest case for that call. - Space
- O(n)
DisjointSets.__init__allocatesparent,rank, andsize, each holdingnentries.
684. Redundant Connection
Process each edge with union(a, b). When the two endpoints are already in the same component, the edge is redundant -- record it as result. Unlike Graph Valid Tree there is no early exit; we continue to find the last such edge. Return result after all edges.
- Time
- O(E · α(n))
- Each of the
Eedges triggers oneunioncall costingO(α(n))amortized (inverse Ackermann, from path compression + union by rank). - All
Eedges are always processed (no early exit), so the worst case isE · α(n). - Space
- O(2n)
DisjointSetsallocates a separateparentarray andrankarray, each of sizen-2n.
Component Size
Journey to the Moon
Group astronauts into nationality-based components with DSU. The answer is the total pairs C(n, 2) minus the sum of same-country pairs C(k, 2) for each component of size k.
- Time
- O(E · α(n) + n)
Ecalls tounion, eachO(α(n))amortized thanks to path compression (infind) and union by rank.getSizesmakes a singleO(n)pass over theparentarray to yield each component's root and size.- Space
- O(n)
- The DSU's
parent,rank, andsizearrays are each sizedn.
Merging Communities
Initialize a DSU over n+1 elements (1-indexed). For each M query, union(i, j) attaches the smaller-rank tree under the larger, updating size[root]. For each Q query, getSize(i) calls find(i) with path compression to reach the root, then returns size[root] -- the community's current population.
- Time
- O(Q * α(n))
Qis the number of queries,nthe number of elements. EachMquery runsunion, and eachQquery'sgetSizecallsfindinternally - both areO(α(n))amortized (path compression + union by rank), soQqueries totalQ * α(n).- Space
- O(n)
- The DSU's
parent,rank, andsizearrays are each sizedn.
Components in a graph
Initialize a DSU of size 2 * len(gb) + 1 to cover all node labels. Union every edge. Iterate roots with getSizes() -- skip singletons (size < 2) and track the running minimum and maximum among the rest.
- Time
- O(E · α(E))
Eis the number of edges ingb.unionis called once per edge, each costingO(α(E))amortized thanks to union-by-rank and path compression.getSizes()then does a singleO(E)pass over the DSU's2E + 1slots to scan component sizes.- Space
- O(E)
- The DSU's
parent,rank, andsizearrays are each sized2E + 1.
Advanced Applications
1202. Smallest String With Swaps
Indices connected directly or transitively through pairs form a component -- characters within a component can be freely rearranged. Union all pairs to identify components, sort each component's characters, then place them back at the component's original indices in ascending order.
- Time
- O((E + V) * α(V) + V log V)
- Unioning all
Epairs, each afind+unioncall, costsO(E * α(V))amortized, whereV = len(s)is the number of characters/indices. - Building
componentsand the final reconstruction loop each callfindonce per index,O(V * α(V))amortized. sorted(components[comp], reverse=True)sorts every component's characters; summed across all components this isO(V log V)in the worst case (one component holding allVcharacters).- Space
- O(sort + V)
parent,rank, andsizeinDisjointSetsare each lengthV.componentsholds every character exactly once across its lists, andresultcollects allVoutput characters.- Sorting algorithms are typically
O(log n)space (in-place, recursion stack only), but Python'slist.sort()is Timsort, which allocates up toO(n)auxiliary space in the worst case - that's whatsortstands for here.
721. Accounts Merge
Use account indices (0..n-1) as DSU elements. Build email_idx mapping each email to the index of the first account that introduced it. For each account, union email_idx[emails[0]] with email_idx[email] for every email -- when emails[0] was seen in an earlier account, this bridges the two account indices into one component. Then group emails by their root account index, sort each group, and prepend the account name.
- Time
- O(N*K*a(N) + N*K*log(N*K))
Nis the number of accounts,Kis the max emails per account. The first loop unions and finds over up toN*Kemails, costingO(N*K*a(N))amortized, whereais the inverse Ackermann function.- The final loop sorts the emails within each component, costing
O(N*K*log(N*K))in the worst case. - Space
- O(sort + N + N*K)
disjoint_sets's parent/rank/size arrays are sizeN.email_idxandmerged_accountshold up toN*Kentries total.- Sorting algorithms are typically
O(log n)space (in-place, recursion stack only), but Python'slist.sort()is Timsort, which allocates up toO(n)auxiliary space in the worst case - that's whatsortstands for here.
947. Most Stones Removed with Same Row or Column
Map each stone to a connection between its row index and a shifted column index (col + 10001). Union the row and column nodes for every stone. Stones that share a row or column fall into the same DSU component. From a component of size k, exactly k - 1 stones can be removed (keep one). The answer is len(stones) - components, where components counts distinct roots among all nodes marked by a stone.
The key trick is using a single DSU of size 20002: rows occupy indices 0-9999 and columns occupy 10001-20001, so they never collide.
- Time
- O(n · α(n))
- The
for row, col in stonesloop performs oneunionper stone -ncalls, eachO(α(n))amortized thanks to path compression and union by rank inDisjointSets- givingn · α(n). - The
components = sum(...)pass iterates over the fixed-size DSU (range(20002)), a constantO(1)pass independent ofn. - Space
- O(1)
disjoint_setsandis_stoneare always allocated at the fixed size20002(bounded by the coordinate range 0-10000, shifted), regardless of the number of stonesn- constant space.
737. Sentence Similarity II
Words in similarPairs that are directly or transitively connected form a component. Assign each unique word a stable DSU index using setdefault (first occurrence wins), then union every pair. For each position in zip(sentence1, sentence2), if the words differ, look both up in pair_idx -- if either is missing or they have different roots, return False. After the zip, check equal lengths.
- Time
- O(P * α(P) + S * α(P))
Pis the number ofsimilarPairs,Sislen(sentence1),αis the inverse Ackermann function.- The first loop builds
pair_idxand callsdisjoint_sets.uniononce per pair -O(P * α(P))amortized. - The second loop walks
Sword pairs, each doing at most twodisjoint_sets.findcalls -O(S * α(P))amortized. - Space
- O(P)
disjoint_sets.parent,.rank, and.sizeare each sized2 * len(similarPairs), soO(P).pair_idxholds at most2 * len(similarPairs)entries, alsoO(P).
990. Satisfiability of Equality Equations
First pass: iterate equations and union every == pair. DSU handles transitivity automatically -- if a==b and b==c, after both unions a and c share a root. Second pass: for each != pair, call find on both letters. If they share a root, some earlier == chain connected them, which directly contradicts the inequality -- return False. If every != pair has distinct roots, return True.
- Time
- O(E)
- DSU size is fixed at 26 (one node per lowercase letter), so every
find/unioncall runs inO(alpha(26)), effectivelyO(1). - Two passes over the
Eequations, each doingO(1)DSU work per equation, giveO(E)total. - Space
- O(1)
- The DSU's
parentandrankarrays are fixed at size 26, regardless ofE.