mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 16:26:47 +08:00
merge: add UnionFind.js plus tests (#814)
* Create UnionFind.js * Create UnionFindTest.js * add UnionFind.js plus tests * implement PR comments * implement PR comment * fix codestyle Co-authored-by: Administrator <pi@pglp.noip.me>
This commit is contained in:
80
Search/UnionFind.js
Normal file
80
Search/UnionFind.js
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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 }
|
54
Search/test/UnionFind.test.js
Normal file
54
Search/test/UnionFind.test.js
Normal file
@ -0,0 +1,54 @@
|
||||
import { UnionFind } from '../UnionFind'
|
||||
|
||||
const uf = new UnionFind(5)
|
||||
|
||||
test('should expose .size():', () => {
|
||||
const size = uf.size()
|
||||
expect(size).toBe(5)
|
||||
})
|
||||
|
||||
test('should do .union(num1, num2):', () => {
|
||||
uf.union(1, 2)
|
||||
uf.union(3, 4)
|
||||
uf.union(0, 4)
|
||||
expect(uf.connected(1, 2)).toBe(true)
|
||||
expect(uf.connected(1, 2)).toBe(true)
|
||||
|
||||
expect(uf.connected(3, 4)).toBe(true)
|
||||
expect(uf.connected(3, 0)).toBe(true)
|
||||
expect(uf.connected(4, 0)).toBe(true)
|
||||
|
||||
expect(uf.connected(1, 3)).toBe(false)
|
||||
expect(uf.connected(1, 4)).toBe(false)
|
||||
expect(uf.connected(1, 0)).toBe(false)
|
||||
expect(uf.connected(2, 3)).toBe(false)
|
||||
expect(uf.connected(2, 4)).toBe(false)
|
||||
expect(uf.connected(2, 0)).toBe(false)
|
||||
})
|
||||
|
||||
test('.count(), should return the number of disparate groups:', () => {
|
||||
expect(uf.count()).toBe(2)
|
||||
})
|
||||
|
||||
test('should check if two components are connected, .connected(num1, num2):', () => {
|
||||
expect(uf.connected(1, 2)).toBe(true)
|
||||
expect(uf.connected(1, 3)).toBe(false)
|
||||
})
|
||||
|
||||
test('should find the root of the tree in which the given element lives, .find(num):', () => {
|
||||
expect(uf.find(1)).toBe(1)
|
||||
expect(uf.find(2)).toBe(1)
|
||||
expect(uf.find(3)).toBe(3)
|
||||
expect(uf.find(4)).toBe(3)
|
||||
expect(uf.find(0)).toBe(3)
|
||||
})
|
||||
|
||||
test('should always change the id of the smaller tree and preserve the id of the larger one', () => {
|
||||
uf.union(2, 3)
|
||||
expect(uf.count()).toBe(1)
|
||||
expect(uf.find(0)).toBe(3)
|
||||
expect(uf.find(1)).toBe(3)
|
||||
expect(uf.find(2)).toBe(3)
|
||||
expect(uf.find(3)).toBe(3)
|
||||
expect(uf.find(4)).toBe(3)
|
||||
})
|
10992
package-lock.json
generated
10992
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@
|
||||
"devDependencies": {
|
||||
"babel-jest": "^26.3.0",
|
||||
"globby": "^12.0.2",
|
||||
"jest": "^26.4.2",
|
||||
"jest": "^26.6.3",
|
||||
"standard": "^16.0.4"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user