Disjoint set (Union-Find) in Data Structures Theory - Time & Space Complexity
We want to understand how the time needed to perform union and find operations changes as the number of elements grows in a Disjoint Set (Union-Find) data structure.
How does the cost of these operations scale when we have more elements to manage?
Analyze the time complexity of the following Union-Find operations with path compression and union by rank.
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # path compression
return self.parent[x]
def union(self, x, y):
rootX = self.find(x)
rootY = self.find(y)
if rootX != rootY:
if self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
elif self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1
This code manages groups of elements, quickly finding which group an element belongs to and joining groups together.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The
findfunction uses recursion to climb up the parent links until it finds the root. - How many times: Each
findcall may traverse up to the height of the tree, but path compression flattens the tree over time. - Union calls use
findtwice and then link roots, which is a constant time operation after finds.
Initially, trees may be tall, so find takes longer. But path compression quickly flattens trees, making future finds faster.
| Input Size (n) | Approx. Operations per find/union |
|---|---|
| 10 | Very few steps, close to 1 |
| 100 | Few steps, slightly more than 1 |
| 1000 | Still very close to 1 due to flattening |
Pattern observation: The cost grows very slowly and almost stays constant for large inputs because of path compression and union by rank.
Time Complexity: O(α(n))
This means each operation takes almost constant time, where α(n) is the inverse Ackermann function, which grows so slowly it can be treated as a tiny constant for all practical input sizes.
[X] Wrong: "Each find operation always takes time proportional to the number of elements because it might traverse the whole chain."
[OK] Correct: Path compression flattens the structure during finds, so after some operations, the trees become very shallow, making finds almost constant time.
Understanding the efficient time complexity of Union-Find shows you can analyze how clever improvements affect performance, a valuable skill in many coding problems.
"What if we removed path compression from the find operation? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of Disjoint Set
Disjoint Set is designed to manage groups of elements and quickly find which group an element belongs to.Step 2: Compare with other options
Sorting, key-value storage, and arithmetic are unrelated to Disjoint Set's main function.Final Answer:
To efficiently keep track of which elements belong to which groups -> Option CQuick Check:
Disjoint Set = group tracking [OK]
- Confusing Disjoint Set with sorting algorithms
- Thinking it stores key-value pairs like a dictionary
- Assuming it performs arithmetic on sets
a and b in a Disjoint Set?Solution
Step 1: Identify standard Disjoint Set operation names
The common and standard name for merging two sets isunion.Step 2: Check other options
Other names likeunionSet,merge, orcombineare not standard and may cause confusion.Final Answer:
union(a, b) -> Option BQuick Check:
Union operation = union(a, b) [OK]
- Using non-standard method names
- Confusing union with find
- Assuming merge or combine are standard
union(1, 2) union(3, 4) union(2, 3) find(4)
What is the result of
find(4)?Solution
Step 1: Perform unions step-by-step
union(1, 2) merges sets {1} and {2} -> {1, 2}. union(3, 4) merges {3} and {4} -> {3, 4}. union(2, 3) merges sets containing 2 and 3 -> {1, 2, 3, 4}.Step 2: Find the representative of element 4
After unions, all elements are in one set. The representative is usually the root of the first union, which is 1.Final Answer:
1 -> Option DQuick Check:
find(4) = 1 after unions [OK]
- Assuming find(4) returns 4 itself
- Not merging sets correctly in union(2, 3)
- Confusing element with its set representative
def union(a, b):
rootA = find(a)
rootB = find(b)
if rootA != rootB:
rootA = rootB # Incorrect assignmentSolution
Step 1: Analyze the union operation
The union operation should update the parent of one root to the other to merge sets.Step 2: Identify the error in assignment
AssigningrootA = rootBonly changes the local variable, not the parent array that tracks sets.Final Answer:
The assignment does not update the parent of rootA -> Option AQuick Check:
Union must update parent array, not local variables [OK]
- Assigning roots instead of updating parent array
- Confusing equality check in if condition
- Expecting union to return a value
union(1, 2), union(3, 4), and union(2, 3), which of the following statements is true?Solution
Step 1: Track unions step-by-step
union(1, 2) merges {1, 2}. union(3, 4) merges {3, 4}. union(2, 3) merges sets containing 2 and 3 -> {1, 2, 3, 4}.Step 2: Check element 5's set
Element 5 was never unioned, so it remains separate.Final Answer:
Elements 1 and 4 belong to the same set -> Option AQuick Check:
1 and 4 connected; 5 separate [OK]
- Assuming all elements merge after some unions
- Thinking element 5 is connected without union
- Confusing which elements are merged
Ready to prove it?
Pass the test and this pattern moves unknown
