quick-find-union-find
Version:
Quick Find Union Find using ES next
155 lines (143 loc) • 5.11 kB
JavaScript
/**
* QuickFindUF is based on
* http://algs4.cs.princeton.edu/15uf/QuickFindUF.java.html
*
* The QuickFindUF class represents a `union–find data type`
* (also known as the `disjoint-sets data type`).
* It supports the `union` and `find` operations,
* along with a `connected` operation for determining whether
* two sites are in the same component and a `count` operation that
* returns the total number of components.
*
* The union–find data type models connectivity among a set of `n`
* sites, named 0 through `n`–1.
* The `is-connected-to` relation must be an
* `equivalence relation`:
*
* - `Reflexive`: `p` is connected to `p`.
* - `Symmetric`: If `p` is connected to `q`,
* then `q` is connected to `p`.
* - `Transitive`: If `p` is connected to `q`
* and `q` is connected to `r`, then
* `p` is connected to `r`.
*
* An equivalence relation partitions the sites into
* `equivalence classes` (or `components`). In this case,
* two sites are in the same component if and only if they are connected.
* Both sites and components are identified with integers between 0 and
* `n`–1.
* Initially, there are `n` components, with each site in its
* own component. The `component identifier` of a component
* (also known as the `root`, `canonical element`, `leader`,
* or `set representative`) is one of the sites in the component:
* two sites have the same component identifier if and only if they are
* in the same component.
*
* - `union`(`p`, `q`) adds a
* connection between the two sites `p` and `q`.
* If `p` and `q` are in different components,
* then it replaces
* these two components with a new component that is the union of
* the two.
* - `find`(`p`) returns the component
* identifier of the component containing `p`.
* - `connected`(`p`, `q`)
* returns true if both `p` and `q`
* are in the same component, and false otherwise.
* - `count`() returns the number of components.
*
*
* The component identifier of a component can change
* only when the component itself changes during a call to
* `union`—it cannot change during a call
* to `find`, `connected`, or `count`.
*
* This implementation uses quick find.
* Initializing a data structure with `n` sites takes linear time.
* Afterwards, the `find`, `connected`, and `count`
* operations take constant time but the `union` operation
* takes linear time.
*
* For additional documentation, see <a href="http://algs4.cs.princeton.edu/15uf">Section 1.5</a> of
* <i>Algorithms, 4th Edition</i> by Robert Sedgewick and Kevin Wayne.
*/
'use strict';
class QuickFindUF {
/**
* Initializes an empty union–find data structure with `n` sites
* `0 through `n-1`. Each site is initially in its own
* component.
*
* @param n the number of sites
* @throws RangeError: Invalid array length if `n < 0`
*/
constructor(n) {
this._id = new Array(n); // id[i] = component identifier of i
this._count = n; // number of components
for (let i = 0; i < n; i++) {
this._id[i] = i;
}
}
/**
* Returns the number of components.
*
* @return the number of components (between `1` and `n`)
*/
get count() {
return this._count;
}
/**
* Returns the component identifier for the component containing site `p`.
*
* @param p the integer representing one site
* @return the component identifier for the component containing site `p`
* @throws Index Out Of Bounds Exception unless `0 <= p < n`
*/
find(p) {
this.validate(p);
return this._id[p];
}
// validate that p is a valid index
validate(p) {
let n = this._id.length;
if (p < 0 || p >= n) {
throw `Index Out Of Bounds. (index ${p} is not between 0 and ${n-1})`;
}
}
/**
* Returns true if the the two sites are in the same component.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @return `true` if the two sites `p` and `q` are in the same component;
* `false` otherwise
* @throws Index Out Of Bounds Exception unless
* both `0 <= p < n` and `0 <= q < n`
*/
connected(p, q) {
this.validate(p);
this.validate(q);
return this._id[p] == this._id[q];
}
/**
* Merges the component containing site `p` with the
* the component containing site `q`.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @throws IndexOutOfBoundsException unless
* both `0 <= p < n` and `0 <= q < n`
*/
union(p, q) {
this.validate(p);
this.validate(q);
let pID = this._id[p]; // needed for correctness
let qID = this._id[q]; // to reduce the number of array accesses
// p and q are already in the same component
if (pID == qID) return;
for (let i = 0; i < this._id.length; i++)
if (this._id[i] == pID) this._id[i] = qID;
this._count--;
}
}
module.exports = QuickFindUF;