Skip to content

Commit a6d97d2

Browse files
committed
[breaking] ⚡ improve(findset): use Unit32Array instead of number[] to save memory and get better performance
1 parent a93bc91 commit a6d97d2

File tree

4 files changed

+85
-48
lines changed

4 files changed

+85
-48
lines changed

Diff for: packages/findset/__test__/findset.spec.ts

+43-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99

1010
describe('createfindset', function () {
1111
const MAX_N = 1000
12-
const findset = createFindset()
12+
const findset = createFindset(MAX_N)
1313

1414
test('basic', function () {
1515
findset.init(MAX_N)
@@ -47,12 +47,21 @@ describe('createfindset', function () {
4747
expect(() => findset.root(MAX_N + 1)).toThrow(/Out of boundary/)
4848

4949
for (let i = 1; i <= MAX_N; ++i) expect(findset.root(i)).toBe(i)
50+
51+
expect(() => findset.init(0)).toThrow(
52+
/Invalid value, expect an integer in the range of/,
53+
)
54+
expect(() => findset.init(1)).not.toThrow()
55+
expect(() => findset.init(MAX_N)).not.toThrow()
56+
expect(() => findset.init(MAX_N + 1)).toThrow(
57+
/Invalid value, expect an integer in the range of/,
58+
)
5059
})
5160
})
5261

5362
describe('createHeuristicfindset', function () {
5463
const MAX_N = 1000
55-
const findset = createHeuristicFindset()
64+
const findset = createHeuristicFindset(MAX_N)
5665

5766
test('basic', function () {
5867
findset.init(MAX_N)
@@ -92,6 +101,15 @@ describe('createHeuristicfindset', function () {
92101
expect(() => findset.root(MAX_N + 1)).toThrow(/Out of boundary/)
93102

94103
for (let i = 1; i <= MAX_N; ++i) expect(findset.root(i)).toBe(i)
104+
105+
expect(() => findset.init(0)).toThrow(
106+
/Invalid value, expect an integer in the range of/,
107+
)
108+
expect(() => findset.init(1)).not.toThrow()
109+
expect(() => findset.init(MAX_N)).not.toThrow()
110+
expect(() => findset.init(MAX_N + 1)).toThrow(
111+
/Invalid value, expect an integer in the range of/,
112+
)
95113
})
96114

97115
test('size', function () {
@@ -111,7 +129,8 @@ describe('createHeuristicfindset', function () {
111129
})
112130

113131
describe('enhanced-findset', function () {
114-
const findset = createEnhancedFindset(10)
132+
const MAX_N = 10
133+
const findset = createEnhancedFindset(MAX_N)
115134

116135
test('init', function () {
117136
findset.init(5)
@@ -121,19 +140,10 @@ describe('enhanced-findset', function () {
121140
for (let x = 6; x <= 10; ++x) {
122141
expect(Array.from(findset.getSetOf(x)!)).toEqual([])
123142
}
124-
125-
expect(() => findset.init(0)).toThrow(
126-
/Invalid value, expect an integer in the range of/,
127-
)
128-
expect(() => findset.init(1)).not.toThrow()
129-
expect(() => findset.init(10)).not.toThrow()
130-
expect(() => findset.init(11)).toThrow(
131-
/Invalid value, expect an integer in the range of/,
132-
)
133143
})
134144

135145
test('merge', function () {
136-
findset.init(10)
146+
findset.init(MAX_N)
137147
findset.merge(2, 3)
138148
expect(findset.size(2)).toEqual(2)
139149
expect(findset.size(3)).toEqual(2)
@@ -151,6 +161,26 @@ describe('enhanced-findset', function () {
151161
expect(Array.from(findset.getSetOf(1)!).sort()).toEqual([1, 2, 3])
152162
expect(Array.from(findset.getSetOf(2)!).sort()).toEqual([1, 2, 3])
153163
})
164+
165+
test('out of boundary', function () {
166+
findset.init(MAX_N)
167+
for (let i = 1; i <= MAX_N; ++i) expect(findset.root(i)).toBe(i)
168+
169+
expect(() => findset.root(-1)).toThrow(/Out of boundary/)
170+
expect(() => findset.root(0)).toThrow(/Out of boundary/)
171+
expect(() => findset.root(MAX_N + 1)).toThrow(/Out of boundary/)
172+
173+
for (let i = 1; i <= MAX_N; ++i) expect(findset.root(i)).toBe(i)
174+
175+
expect(() => findset.init(0)).toThrow(
176+
/Invalid value, expect an integer in the range of/,
177+
)
178+
expect(() => findset.init(1)).not.toThrow()
179+
expect(() => findset.init(MAX_N)).not.toThrow()
180+
expect(() => findset.init(MAX_N + 1)).toThrow(
181+
/Invalid value, expect an integer in the range of/,
182+
)
183+
})
154184
})
155185

156186
describe('leetcode', function () {

Diff for: packages/findset/src/enhanced.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ export function createEnhancedFindset(MAX_N: number): EnhancedFindset {
4848
}
4949

5050
function root(x: number): number {
51-
// eslint-disable-next-line no-return-assign
52-
return pa[x] === x ? x : (pa[x] = root(pa[x]))
51+
if (x < 1 || x > MAX_N)
52+
throw new RangeError(`Out of boundary [1, ${MAX_N}]. x: ${x}`)
53+
return _root(x)
5354
}
5455

5556
function merge(x: number, y: number): number {
@@ -87,4 +88,9 @@ export function createEnhancedFindset(MAX_N: number): EnhancedFindset {
8788
const xx: number = root(x)
8889
return sets[xx]
8990
}
91+
92+
function _root(x: number): number {
93+
// eslint-disable-next-line no-return-assign
94+
return pa[x] === x ? x : (pa[x] = _root(pa[x]))
95+
}
9096
}

Diff for: packages/findset/src/heuristic.ts

+17-19
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,29 @@ export interface HeuristicFindset extends Findset {
2121
* Create a heuristic find set.
2222
* @returns
2323
*/
24-
export function createHeuristicFindset(): HeuristicFindset {
25-
const parent: number[] = []
24+
export function createHeuristicFindset(MAX_N: number): HeuristicFindset {
25+
const pa: Uint32Array = new Uint32Array(MAX_N + 1)
2626
const count: number[] = []
27-
28-
let _size = 0
2927
return { init, initNode, root, merge, size }
3028

3129
function init(N: number): void {
32-
_size = N
33-
if (parent.length <= _size) parent.length = _size + 1
34-
if (count.length <= _size) count.length = _size + 1
35-
for (let i = 1; i <= _size; ++i) {
36-
parent[i] = i
37-
count[i] = 1
30+
if (N < 1 || N > MAX_N) {
31+
throw new TypeError(
32+
`Invalid value, expect an integer in the range of [1, ${MAX_N}], but got ${N}.`,
33+
)
3834
}
35+
36+
for (let x = 1; x <= N; ++x) initNode(x)
3937
}
4038

4139
function initNode(x: number): void {
42-
parent[x] = x
40+
pa[x] = x
4341
count[x] = 1
4442
}
4543

46-
function root(x: number): number | never {
47-
if (x < 1 || x > _size)
48-
throw new RangeError(`Out of boundary [1, ${_size}]. x: ${x}`)
44+
function root(x: number): number {
45+
if (x < 1 || x > MAX_N)
46+
throw new RangeError(`Out of boundary [1, ${MAX_N}]. x: ${x}`)
4947
return _root(x)
5048
}
5149

@@ -55,11 +53,12 @@ export function createHeuristicFindset(): HeuristicFindset {
5553
if (xx === yy) return xx
5654

5755
if (count[xx] > count[yy]) {
58-
parent[yy] = xx
56+
pa[yy] = xx
5957
count[xx] += count[yy]
6058
return xx
6159
}
62-
parent[xx] = yy
60+
61+
pa[xx] = yy
6362
count[yy] += count[xx]
6463
return yy
6564
}
@@ -69,9 +68,8 @@ export function createHeuristicFindset(): HeuristicFindset {
6968
return count[xx]
7069
}
7170

72-
function _root(x: number): number | never {
73-
if (parent[x] === x) return x
71+
function _root(x: number): number {
7472
// eslint-disable-next-line no-return-assign
75-
return (parent[x] = _root(parent[x]))
73+
return pa[x] === x ? x : (pa[x] = _root(pa[x]))
7674
}
7775
}

Diff for: packages/findset/src/ordinary.ts

+17-14
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,27 @@ export interface Findset {
4141
* Create a find set.
4242
* @returns
4343
*/
44-
export function createFindset(): Findset {
45-
const parent: number[] = []
46-
let _size = 0
44+
export function createFindset(MAX_N: number): Findset {
45+
const pa: Uint32Array = new Uint32Array(MAX_N + 1)
4746
return { init, initNode, root, merge }
4847

4948
function init(N: number): void {
50-
_size = N
51-
if (parent.length <= _size) parent.length = _size + 1
52-
for (let i = 1; i <= N; ++i) parent[i] = i
49+
if (N < 1 || N > MAX_N) {
50+
throw new TypeError(
51+
`Invalid value, expect an integer in the range of [1, ${MAX_N}], but got ${N}.`,
52+
)
53+
}
54+
55+
for (let x = 1; x <= N; ++x) initNode(x)
5356
}
5457

5558
function initNode(x: number): void {
56-
parent[x] = x
59+
pa[x] = x
5760
}
5861

5962
function root(x: number): number {
60-
if (x < 1 || x > _size)
61-
throw new RangeError(`Out of boundary [1, ${_size}]. x: ${x}`)
63+
if (x < 1 || x > MAX_N)
64+
throw new RangeError(`Out of boundary [1, ${MAX_N}]. x: ${x}`)
6265
return _root(x)
6366
}
6467

@@ -68,16 +71,16 @@ export function createFindset(): Findset {
6871
if (xx === yy) return xx
6972

7073
if (xx < yy) {
71-
parent[yy] = xx
74+
pa[yy] = xx
7275
return xx
7376
}
74-
parent[xx] = yy
77+
78+
pa[xx] = yy
7579
return yy
7680
}
7781

78-
function _root(x: number): number | never {
79-
if (parent[x] === x) return x
82+
function _root(x: number): number {
8083
// eslint-disable-next-line no-return-assign
81-
return (parent[x] = _root(parent[x]))
84+
return pa[x] === x ? x : (pa[x] = _root(pa[x]))
8285
}
8386
}

0 commit comments

Comments
 (0)