class UnionFind: def __init__(self, size): self.parent = list(range(size)) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] uf = UnionFind(5) uf.parent = [0, 0, 1, 2, 3] print(uf.find(4)) print(uf.parent)
The find function recursively finds the root of the element and updates the parent of each node along the path to point directly to the root. Initially, uf.parent is [0, 0, 1, 2, 3]. Calling find(4) traces the path 4 -> 3 -> 2 -> 1 -> 0. After path compression, all these nodes point directly to 0, so uf.parent becomes [0, 0, 0, 0, 0]. The root returned is 0.
Calling find(3) follows the path 3 -> 2 -> 1 -> 0. After path compression, nodes 3, 2, and 1 will point directly to 0. So the parent array updates to [0, 0, 0, 0, 3].
class UnionFind: def __init__(self, size): self.parent = list(range(size)) def find(self, x): if self.parent[x] == x: return x self.parent[x] = self.find(self.parent[x]) uf = UnionFind(5) uf.parent = [0, 0, 1, 2, 3] print(uf.find(3))
The find method is missing a return statement after the recursive call. So when self.parent[x] != x, the function returns None implicitly. Thus, uf.find(3) returns None.
class UnionFind: def __init__(self, size): self.parent = list(range(size)) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, a, b): rootA = self.find(a) rootB = self.find(b) if rootA != rootB: self.parent[rootB] = rootA uf = UnionFind(6) uf.union(0, 1) uf.union(1, 2) uf.union(3, 4) uf.union(4, 5) uf.find(5) print(uf.parent)
Unions connect 0-1-2 and 3-4-5. After uf.find(5), path compression updates parent of 5 to 3. The parent array is [0, 0, 0, 3, 3, 3].
Path compression optimizes the find operation so that over many operations, the average time per operation is almost constant, bounded by the inverse Ackermann function, which grows extremely slowly.
