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; |