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
# Example usage
ds = DisjointSet(5) # Elements 0 to 4
# Initially, all are separate
print(ds.find(0)) # 0
print(ds.find(1)) # 1
# Union sets containing 0 and 1
ds.union(0, 1)
print(ds.find(0)) # Same as find(1)
print(ds.find(1)) # Same as find(0)
# Union sets containing 3 and 4
ds.union(3, 4)
print(ds.find(3)) # Same as find(4)
# Union sets containing 1 and 3 (merging two groups)
ds.union(1, 3)
print(ds.find(0)) # Same representative for 0,1,3,4
print(ds.find(4)) # Same representative for 0,1,3,4