Path Compression in Union Find in DSA Go - Time & Space Complexity
We want to understand how fast the "find" operation runs when using path compression in Union Find.
Specifically, how does the time grow as we have more elements and unions?
Analyze the time complexity of the find function with path compression.
func find(parent []int, x int) int {
if parent[x] != x {
parent[x] = find(parent, parent[x])
}
return parent[x]
}
This code finds the root of x and compresses the path by pointing x directly to the root.
Look for repeated steps in the find function.
- Primary operation: Recursive calls following parent links up the tree.
- How many times: At most once per level from x to root, but path compression shortens future calls.
Initially, trees can be tall, so find may take several steps.
| Input Size (n) | Approx. Operations per find |
|---|---|
| 10 | Up to 4-5 steps, quickly reduced by compression |
| 100 | Up to 10-15 steps initially, then fewer |
| 1000 | Up to 20-30 steps initially, then much fewer |
Pattern observation: The steps grow very slowly and flatten quickly due to path compression.
Time Complexity: O(α(n))
This means each find runs almost in constant time, where α(n) is a very slow-growing function (inverse Ackermann function).
[X] Wrong: "Find always takes linear time because it may traverse many parents."
[OK] Correct: Path compression flattens the tree, so repeated finds become very fast, making the average cost almost constant.
Understanding path compression's time complexity shows you can analyze clever optimizations that make algorithms efficient in practice.
"What if we removed path compression? How would the time complexity of find change then?"
Practice
path compression in the Union Find data structure?Solution
Step 1: Understand the role of path compression
Path compression is used during thefindoperation to flatten the tree structure by making nodes point directly to the root.Step 2: Compare with other operations
Merging sets by size is done in union by size/rank, not path compression. Initialization and counting components are unrelated to path compression.Final Answer:
To speed up thefindoperation by making nodes point directly to the root -> Option AQuick Check:
Path compression optimizes find = To speed up thefindoperation by making nodes point directly to the root [OK]
- Confusing path compression with union by size/rank
- Thinking it initializes the data structure
- Believing it counts components
find function with path compression in Union Find?Solution
Step 1: Identify path compression in the code
func find(parent []int, x int) int { if parent[x] != x { parent[x] = find(parent, parent[x]) } return parent[x] } recursively updatesparent[x]to the root, which is path compression.Step 2: Check other options for correctness
func find(parent []int, x int) int { for parent[x] != x { x = parent[x] } return x } uses a loop but no path compression. func find(parent []int, x int) int { if parent[x] == x { return x } else { return find(parent, x) } } calls find without updatingparent[x]. func find(parent []int, x int) int { parent[x] = x; return x } wrongly resetsparent[x]to itself always.Final Answer:
func find(parent []int, x int) int { if parent[x] != x { parent[x] = find(parent, parent[x]) } return parent[x] } -> Option DQuick Check:
Recursive update of parent[x] = find(...) = func find(parent []int, x int) int { if parent[x] != x { parent[x] = find(parent, parent[x]) } return parent[x] } [OK]
- Missing parent update in recursion
- Using loop without compression
- Resetting parent incorrectly
package main
import "fmt"
func find(parent []int, x int) int {
if parent[x] != x {
parent[x] = find(parent, parent[x])
}
return parent[x]
}
func main() {
parent := []int{0, 1, 2, 3, 4}
parent[1] = 0
parent[2] = 1
fmt.Println(find(parent, 2))
fmt.Println(parent)
}Solution
Step 1: Trace the find call for element 2
find(2) calls find(1), which calls find(0). Since parent[0] = 0, root is 0.Step 2: Apply path compression updates
parent[2] is updated to 0, parent[1] is updated to 0. Final parent array is [0 0 0 3 4].Final Answer:
0 [0 0 0 3 4] -> Option BQuick Check:
Root of 2 is 0 and parent array updated = 0 [0 0 0 3 4] [OK]
- Not updating parent array after find
- Returning intermediate parent instead of root
- Confusing indices in parent array
find with path compression:
func find(parent []int, x int) int {
if parent[x] == x {
return x
}
parent[x] = find(parent, x)
return parent[x]
}Solution
Step 1: Analyze the recursive call argument
The recursive call usesfind(parent, x), which causes infinite recursion becausexdoes not change.Step 2: Correct the recursive call
It should callfind(parent, parent[x])to move up the tree towards the root.Final Answer:
The recursive call should befind(parent, parent[x])instead offind(parent, x)-> Option CQuick Check:
Recursive call must move up tree = The recursive call should befind(parent, parent[x])instead offind(parent, x)[OK]
- Calling find with same x causing infinite recursion
- Wrong base case condition
- Not updating parent[x] for compression
parent = [0, 0, 1, 2, 3], what will be the parent array after calling find(parent, 4) with path compression?Solution
Step 1: Trace the find call for element 4
find(4) calls find(3), find(3) calls find(2), find(2) calls find(1), find(1) calls find(0). Root is 0.Step 2: Apply path compression updates
During recursion unwind: parent[1] = 0 (already), parent[2] = 0, parent[3] = 0, parent[4] = 0. Final parent array is [0, 0, 0, 0, 0].Final Answer:
[0, 0, 0, 0, 0] -> Option AQuick Check:
All nodes on path updated to root 0 = [0, 0, 0, 0, 0] [OK]
- Only updating parent[4] to root
- Failing to compress the full path
- Confusing parent indices
Ready to prove it?
Pass the test and this pattern moves unknown
