You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
3.6 KiB
196 lines
3.6 KiB
3 years ago
|
/* eslint no-constant-condition: 0 */
|
||
|
/**
|
||
|
* Mnemonist StaticDisjointSet
|
||
|
* ============================
|
||
|
*
|
||
|
* JavaScript implementation of a static disjoint set (union-find).
|
||
|
*
|
||
|
* Note that to remain performant, this implementation needs to know a size
|
||
|
* beforehand.
|
||
|
*/
|
||
|
var helpers = require('./utils/typed-arrays.js');
|
||
|
|
||
|
/**
|
||
|
* StaticDisjointSet.
|
||
|
*
|
||
|
* @constructor
|
||
|
*/
|
||
|
function StaticDisjointSet(size) {
|
||
|
|
||
|
// Optimizing the typed array types
|
||
|
var ParentsTypedArray = helpers.getPointerArray(size),
|
||
|
RanksTypedArray = helpers.getPointerArray(Math.log2(size));
|
||
|
|
||
|
// Properties
|
||
|
this.size = size;
|
||
|
this.dimension = size;
|
||
|
this.parents = new ParentsTypedArray(size);
|
||
|
this.ranks = new RanksTypedArray(size);
|
||
|
|
||
|
// Initializing parents
|
||
|
for (var i = 0; i < size; i++)
|
||
|
this.parents[i] = i;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method used to find the root of the given item.
|
||
|
*
|
||
|
* @param {number} x - Target item.
|
||
|
* @return {number}
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.find = function(x) {
|
||
|
var y = x;
|
||
|
|
||
|
var c, p;
|
||
|
|
||
|
while (true) {
|
||
|
c = this.parents[y];
|
||
|
|
||
|
if (y === c)
|
||
|
break;
|
||
|
|
||
|
y = c;
|
||
|
}
|
||
|
|
||
|
// Path compression
|
||
|
while (true) {
|
||
|
p = this.parents[x];
|
||
|
|
||
|
if (p === y)
|
||
|
break;
|
||
|
|
||
|
this.parents[x] = y;
|
||
|
x = p;
|
||
|
}
|
||
|
|
||
|
return y;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method used to perform the union of two items.
|
||
|
*
|
||
|
* @param {number} x - First item.
|
||
|
* @param {number} y - Second item.
|
||
|
* @return {StaticDisjointSet}
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.union = function(x, y) {
|
||
|
var xRoot = this.find(x),
|
||
|
yRoot = this.find(y);
|
||
|
|
||
|
// x and y are already in the same set
|
||
|
if (xRoot === yRoot)
|
||
|
return this;
|
||
|
|
||
|
this.dimension--;
|
||
|
|
||
|
// x and y are not in the same set, we merge them
|
||
|
var xRank = this.ranks[x],
|
||
|
yRank = this.ranks[y];
|
||
|
|
||
|
if (xRank < yRank) {
|
||
|
this.parents[xRoot] = yRoot;
|
||
|
}
|
||
|
else if (xRank > yRank) {
|
||
|
this.parents[yRoot] = xRoot;
|
||
|
}
|
||
|
else {
|
||
|
this.parents[yRoot] = xRoot;
|
||
|
this.ranks[xRoot]++;
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method returning whether two items are connected.
|
||
|
*
|
||
|
* @param {number} x - First item.
|
||
|
* @param {number} y - Second item.
|
||
|
* @return {boolean}
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.connected = function(x, y) {
|
||
|
var xRoot = this.find(x);
|
||
|
|
||
|
return xRoot === this.find(y);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method returning the set mapping.
|
||
|
*
|
||
|
* @return {TypedArray}
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.mapping = function() {
|
||
|
var MappingClass = helpers.getPointerArray(this.dimension);
|
||
|
|
||
|
var ids = {},
|
||
|
mapping = new MappingClass(this.size),
|
||
|
c = 0;
|
||
|
|
||
|
var r;
|
||
|
|
||
|
for (var i = 0, l = this.parents.length; i < l; i++) {
|
||
|
r = this.find(i);
|
||
|
|
||
|
if (typeof ids[r] === 'undefined') {
|
||
|
mapping[i] = c;
|
||
|
ids[r] = c++;
|
||
|
}
|
||
|
else {
|
||
|
mapping[i] = ids[r];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mapping;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method used to compile the disjoint set into an array of arrays.
|
||
|
*
|
||
|
* @return {array}
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.compile = function() {
|
||
|
var ids = {},
|
||
|
result = new Array(this.dimension),
|
||
|
c = 0;
|
||
|
|
||
|
var r;
|
||
|
|
||
|
for (var i = 0, l = this.parents.length; i < l; i++) {
|
||
|
r = this.find(i);
|
||
|
|
||
|
if (typeof ids[r] === 'undefined') {
|
||
|
result[c] = [i];
|
||
|
ids[r] = c++;
|
||
|
}
|
||
|
else {
|
||
|
result[ids[r]].push(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Convenience known methods.
|
||
|
*/
|
||
|
StaticDisjointSet.prototype.inspect = function() {
|
||
|
var array = this.compile();
|
||
|
|
||
|
// Trick so that node displays the name of the constructor
|
||
|
Object.defineProperty(array, 'constructor', {
|
||
|
value: StaticDisjointSet,
|
||
|
enumerable: false
|
||
|
});
|
||
|
|
||
|
return array;
|
||
|
};
|
||
|
|
||
|
if (typeof Symbol !== 'undefined')
|
||
|
StaticDisjointSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = StaticDisjointSet.prototype.inspect;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Exporting.
|
||
|
*/
|
||
|
module.exports = StaticDisjointSet;
|