Spaces:
Sleeping
Sleeping
| """ | |
| Union-find data structure. | |
| """ | |
| from networkx.utils import groups | |
| class UnionFind: | |
| """Union-find data structure. | |
| Each unionFind instance X maintains a family of disjoint sets of | |
| hashable objects, supporting the following two methods: | |
| - X[item] returns a name for the set containing the given item. | |
| Each set is named by an arbitrarily-chosen one of its members; as | |
| long as the set remains unchanged it will keep the same name. If | |
| the item is not yet part of a set in X, a new singleton set is | |
| created for it. | |
| - X.union(item1, item2, ...) merges the sets containing each item | |
| into a single larger set. If any item is not yet part of a set | |
| in X, it is added to X as one of the members of the merged set. | |
| Union-find data structure. Based on Josiah Carlson's code, | |
| https://code.activestate.com/recipes/215912/ | |
| with significant additional changes by D. Eppstein. | |
| http://www.ics.uci.edu/~eppstein/PADS/UnionFind.py | |
| """ | |
| def __init__(self, elements=None): | |
| """Create a new empty union-find structure. | |
| If *elements* is an iterable, this structure will be initialized | |
| with the discrete partition on the given set of elements. | |
| """ | |
| if elements is None: | |
| elements = () | |
| self.parents = {} | |
| self.weights = {} | |
| for x in elements: | |
| self.weights[x] = 1 | |
| self.parents[x] = x | |
| def __getitem__(self, object): | |
| """Find and return the name of the set containing the object.""" | |
| # check for previously unknown object | |
| if object not in self.parents: | |
| self.parents[object] = object | |
| self.weights[object] = 1 | |
| return object | |
| # find path of objects leading to the root | |
| path = [] | |
| root = self.parents[object] | |
| while root != object: | |
| path.append(object) | |
| object = root | |
| root = self.parents[object] | |
| # compress the path and return | |
| for ancestor in path: | |
| self.parents[ancestor] = root | |
| return root | |
| def __iter__(self): | |
| """Iterate through all items ever found or unioned by this structure.""" | |
| return iter(self.parents) | |
| def to_sets(self): | |
| """Iterates over the sets stored in this structure. | |
| For example:: | |
| >>> partition = UnionFind("xyz") | |
| >>> sorted(map(sorted, partition.to_sets())) | |
| [['x'], ['y'], ['z']] | |
| >>> partition.union("x", "y") | |
| >>> sorted(map(sorted, partition.to_sets())) | |
| [['x', 'y'], ['z']] | |
| """ | |
| # Ensure fully pruned paths | |
| for x in self.parents: | |
| _ = self[x] # Evaluated for side-effect only | |
| yield from groups(self.parents).values() | |
| def union(self, *objects): | |
| """Find the sets containing the objects and merge them all.""" | |
| # Find the heaviest root according to its weight. | |
| roots = iter( | |
| sorted( | |
| {self[x] for x in objects}, key=lambda r: self.weights[r], reverse=True | |
| ) | |
| ) | |
| try: | |
| root = next(roots) | |
| except StopIteration: | |
| return | |
| for r in roots: | |
| self.weights[root] += self.weights[r] | |
| self.parents[r] = root | |