/** * union find data structure for javascript * * In computer science, a disjoint-set data structure, also called a union–find data structure or merge–find set, * is a data structure that stores a collection of disjoint (non-overlapping) sets. Equivalently, it stores a partition * of a set into disjoint subsets. It provides operations for adding new sets, merging sets (replacing them by their union), * and finding a representative member of a set. * The last operation allows to find out efficiently if any two elements are in the same or different sets. * * Disjoint-set data structures play a key role in Kruskal's algorithm for finding the minimum spanning tree of a graph. * The importance of minimum spanning trees means that disjoint-set data structures underlie a wide variety of algorithms. * In addition, disjoint-set data structures also have applications to symbolic computation, as well in compilers, * especially for register allocation problems. * * you can learn more on disjoint-set / union–find data structure at https://en.wikipedia.org/wiki/Disjoint-set_data_structure */ function UnionFind (n, key) { if (!(this instanceof UnionFind)) return new UnionFind(n) if (key && typeof key !== 'function') { throw new Error('key has to be a function or else left undefined') } let cnt, length // init Union Find with number of distinct groups. Each group will be referred to as index of the array of size 'size' starting at 0. // Provide an optional key function that maps these indices. I.e. for the groups starting with 1 provide function(a){return a-1;}. The default value is function(a){return a;}. key = key || function (a) { return a } cnt = length = n const id = new Array(n) const sz = new Array(n) for (let i = 0; i < n; i++) { id[i] = i sz[i] = 1 } // Returns the number of elements of uf object. this.size = function () { return length } // Returns the number of distinct groups left inside the object. this.count = function () { return cnt } // Return the root (value) of the group in which p is. this.find = function (p) { p = key(p) while (p !== id[p]) { id[p] = id[id[p]] p = id[p] } return p } // Returns true if p and p are both in same group, false otherwise. this.connected = function (p, q) { p = key(p) q = key(q) ensureIndexWithinBounds(p, q) return this.find(p) === this.find(q) } // Combine elements in groups p and q into a single group. In other words connect the two groups. this.union = function (p, q) { p = key(p) q = key(q) ensureIndexWithinBounds(p, q) const i = this.find(p) const j = this.find(q) if (i === j) return if (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i] } else { id[j] = i; sz[i] += sz[j] } cnt-- } function ensureIndexWithinBounds (args) { for (let i = arguments.length - 1; i >= 0; i--) { const p = arguments[i] if (p >= length) throw new Error('Index out of bounds. The maximum index can be length-1') } } } export { UnionFind }