Publicado · en mejora
Guía de Union-Find · 6/6
Por ahora, este capítulo solo está disponible en inglés.
Union-find is not just an interview data structure. It lives inside graph libraries, image processing, compilers and data-cleaning pipelines. This chapter shows how real libraries expose it, lists common mistakes, and points to further reading.
Before writing your own, check whether a library you already use provides one.
scipy.cluster.hierarchy.DisjointSet (since SciPy 1.6) accepts any hashable object as an element, not just integers.networkx.utils.UnionFind is used internally, for example by its Kruskal minimum spanning tree implementation.boost::disjoint_sets, used for incremental connected components.label or OpenCV's connectedComponents relies on the same idea to merge provisional labels in the classic two-pass algorithm.from scipy.cluster.hierarchy import DisjointSet
ds = DisjointSet(["a", "b", "c", "d"])
ds.merge("a", "b")
ds.merge("c", "d")
print(ds.connected("a", "c")) # False
ds.add("e")
ds.merge("b", "e")
print(sorted(map(sorted, ds.subsets()))) # [['a', 'b', 'e'], ['c', 'd']]Real data rarely comes with integer keys. Number each new key with a dictionary and the array-based DSU works unchanged.
class KeyedDSU:
def __init__(self):
self.index, self.parent, self.size = {}, [], []
def _id(self, key):
if key not in self.index:
self.index[key] = len(self.parent)
self.parent.append(len(self.parent))
self.size.append(1)
return self.index[key]
def find(self, key):
x = self._id(key)
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra == rb:
return False
if self.size[ra] < self.size[rb]:
ra, rb = rb, ra
self.parent[rb] = ra
self.size[ra] += self.size[rb]
return True
d = KeyedDSU()
d.union("phone:555-0100", "mail:kim@x.io")
d.union("mail:kim@x.io", "device:A7")
print(d.find("phone:555-0100") == d.find("device:A7")) # Trueparent[a] = b corrupts the sets. Always link the roots returned by find.parent[a] == parent[b] can be wrong before paths are compressed. Always use find(a) == find(b).class RollbackDSU:
def __init__(self, n):
self.parent, self.size, self.history = list(range(n)), [1] * n, []
def find(self, x): # no path compression, so changes stay undoable
while self.parent[x] != x:
x = self.parent[x]
return x
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if self.size[ra] < self.size[rb]:
ra, rb = rb, ra
self.history.append((rb, ra) if ra != rb else None)
if ra != rb:
self.parent[rb] = ra
self.size[ra] += self.size[rb]
def rollback(self):
last = self.history.pop()
if last:
rb, ra = last
self.parent[rb] = rb
self.size[ra] -= self.size[rb]Union-find already powers SciPy, NetworkX and Boost, as well as image labeling, type inference and data cleaning. Three habits prevent most bugs: link roots, compare with find, and implement find with a loop. When you need undo, reach for a rollback DSU.
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.
Sé el primero en comentar.