Source code for src.dsu
[docs]class DisjointSetUnion:
"""
Construct a disjoint set union (DSU)
This data structure tracks a set of elements partitioned into a
number of *disjoint* (i.e. non-overlapping) subsets. It provides
almost constant-time operations to:
1. Add a new element to the set
2. Join two sets into one (union)
3. Find if two elements are in the same set (find)
Alternative names for this data structure:
1. Disjoint-set data structure
2. Union-find data structure
3. Merge-find set
"""
[docs] def __init__(self, xs=None):
"""
Create an empty or a pre-populated disjoint set union (DSU)
:param xs: a list of elements. If None, then an empty DSU is
created. Otherwise, each element is treated as its
own set.
"""
self.xs = {x: x for x in xs} if xs else {}
self.ws = {x: 1 for x in xs} if xs else {}
self.count = len(xs) if xs else 0
def __iter__(self):
return iter(self.xs)
def __getitem__(self, key):
return self.find(key)
def __setitem__(self, key, val):
"""
Add a new element to the disjoint set union (DSU)
The item is added initially as its own set.
:param key: the item to add to the DSU
:param val: the value, must be equal to the provided key
"""
if key is not val:
raise RuntimeError("key and val must be the same")
self.xs[key] = key
self.ws[key] = 1
[docs] def find(self, key, w=0):
"""
Find the set of the provided key
This method returns the same key for those keys that belong to
the same set. Internally, the keys from the same set make up a
tree whose root holds the key that represents the whole set.
When you call find with a specific key, there are two edge
cases:
1. The provided key is, in fact, the root of the tree. If so,
it is immediately returned. 2. The provided key is a leaf of
some very long path to the root of the tree. In this case, the
entire path to the root must be traversed.
To avoid having very long paths, the second case also performs
path compression after traversing the path to the root.
:param key: the key whose set should be found
:param w: the weight of the subtree of the key
"""
if self.xs[key] == key:
self.ws[key] += w
return key
self.ws[self.xs[key]] -= self.ws[key]
self.xs[key] = self.find(self.xs[key], self.ws[key])
return self.xs[key]
[docs] def union(self, key1, key2):
"""
Combine the sets that contain the two given keys into one
If both keys belong to the same set, the method does not
achieve much. However, path compression could still change
the underlying data structure.
:param key1: the key contained in the first set
:param key2: the key contained in the second set
"""
val1 = self.find(key1)
val2 = self.find(key2)
if val1 == val2:
return
if self.ws[val1] < self.ws[val2]:
val1, val2 = val2, val1
self.xs[val1] = val2
self.ws[val2] += self.ws[val1]