Union & Find
Unon & Find is a tree data structure, which deals with finding an elements in the sets and unioning the disjoint sets.
- Union: union 2 sub-sets
- Find: find the sub-set of an element.
It can be used to verify whether 2 elements are in the same set.
Node
The node in Unon & Find has an attribute, parent.
Unon & Find is create by the array.
parent_node = self.parent[curr_node]
Initialization
Unon & Find is initialized by an array, parent.
The parents of initial elements are themselves.
class UnionFind:
def __init__(self, n: int) -> None:
self.parent = [i for i in range(n)]
Find the root
def findRoot(self, i: int) -> int:
root = i
while root != self.parent[root]:
root = self.parent[root]
return root
Compress the path
To improve the performance, reduce the depth of the tree, we compress the path while finding the root.
We set all the nodes under a node as its direct children. It's the recommended way.
def findRoot(self, i: int) -> int:
root = i
while root != self.parent[root]:
root = self.parent[root]
# compress the path
# point sub-nodes to the root directly
while i != root:
self.parent[i], i = root, self.parent[i]
return root
union
Union 2 sets.
def union(self, p: int, q: int) -> None:
p_root = self.findRoot(p)
q_root = self.findRoot(q)
# p_root.parent = q_root
self.parent[p_root] = q_root
We can compress the path here if we didn't do it in the findRoot.
However, we need to use another array, rank. It keeps the depth of the nodes.
We always attach smaller rank tree under root of high rank tree.
def __init__(self, n: int) -> None:
self.parent = [i for i in range(n)]
self.ranks = [0 for _ in range(n)]
def union(self, p: int, q: int) -> None:
p_root = self.findRoot(p)
q_root = self.findRoot(q)
if p_root != q_root:
if self.ranks[p_root] > self.ranks[q_root]:
# q_root.parent = p_root
self.parent[q_root] = p_root
elif self.ranks[p_root] < self.ranks[q_root]:
# p_root.parent = q_root
self.parent[p_root] = q_root
else:
# p_root.parent = q_root
self.parent[p_root] = q_root
self.ranks[p_root] += 1