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