data-structure-typed 1.53.5 → 1.53.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +16 -25
- package/benchmark/report.html +32 -5
- package/benchmark/report.json +326 -23
- package/dist/cjs/common/index.d.ts +12 -0
- package/dist/cjs/common/index.js +24 -0
- package/dist/cjs/common/index.js.map +1 -0
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js +7 -10
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree.js +2 -2
- package/dist/cjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/binary-tree.d.ts +54 -19
- package/dist/cjs/data-structures/binary-tree/binary-tree.js +100 -66
- package/dist/cjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/bst.d.ts +100 -36
- package/dist/cjs/data-structures/binary-tree/bst.js +185 -66
- package/dist/cjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/rb-tree.d.ts +4 -0
- package/dist/cjs/data-structures/binary-tree/rb-tree.js +6 -2
- package/dist/cjs/data-structures/binary-tree/rb-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/heap/heap.d.ts +6 -6
- package/dist/cjs/data-structures/heap/heap.js +6 -6
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.d.ts +31 -19
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js +49 -34
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/singly-linked-list.d.ts +144 -62
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js +201 -97
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/trie/trie.d.ts +110 -4
- package/dist/cjs/data-structures/trie/trie.js +122 -12
- package/dist/cjs/data-structures/trie/trie.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
- package/dist/cjs/types/data-structures/binary-tree/bst.d.ts +3 -2
- package/dist/cjs/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
- package/dist/cjs/types/utils/utils.d.ts +10 -6
- package/dist/cjs/utils/utils.js +4 -2
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/mjs/common/index.d.ts +12 -0
- package/dist/mjs/common/index.js +24 -0
- package/dist/mjs/common/index.js.map +1 -0
- package/dist/mjs/data-structures/binary-tree/avl-tree-multi-map.js +8 -10
- package/dist/mjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/avl-tree.js +3 -2
- package/dist/mjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/binary-tree.d.ts +54 -19
- package/dist/mjs/data-structures/binary-tree/binary-tree.js +95 -61
- package/dist/mjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/bst.d.ts +100 -36
- package/dist/mjs/data-structures/binary-tree/bst.js +187 -66
- package/dist/mjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/rb-tree.d.ts +4 -0
- package/dist/mjs/data-structures/binary-tree/rb-tree.js +6 -2
- package/dist/mjs/data-structures/binary-tree/rb-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/mjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/mjs/data-structures/heap/heap.d.ts +6 -6
- package/dist/mjs/data-structures/heap/heap.js +6 -6
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.d.ts +31 -19
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js +49 -34
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/mjs/data-structures/linked-list/singly-linked-list.d.ts +144 -62
- package/dist/mjs/data-structures/linked-list/singly-linked-list.js +201 -97
- package/dist/mjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/mjs/data-structures/trie/trie.d.ts +110 -4
- package/dist/mjs/data-structures/trie/trie.js +122 -12
- package/dist/mjs/data-structures/trie/trie.js.map +1 -1
- package/dist/mjs/index.d.ts +1 -1
- package/dist/mjs/index.js +1 -1
- package/dist/mjs/index.js.map +1 -1
- package/dist/mjs/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
- package/dist/mjs/types/data-structures/binary-tree/bst.d.ts +3 -2
- package/dist/mjs/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
- package/dist/mjs/types/utils/utils.d.ts +10 -6
- package/dist/mjs/utils/utils.js +4 -2
- package/dist/mjs/utils/utils.js.map +1 -1
- package/dist/umd/data-structure-typed.js +512 -266
- package/dist/umd/data-structure-typed.min.js +2 -2
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +8 -8
- package/src/common/index.ts +19 -0
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -9
- package/src/data-structures/binary-tree/avl-tree.ts +3 -2
- package/src/data-structures/binary-tree/binary-tree.ts +108 -64
- package/src/data-structures/binary-tree/bst.ts +190 -69
- package/src/data-structures/binary-tree/rb-tree.ts +6 -2
- package/src/data-structures/binary-tree/tree-multi-map.ts +3 -3
- package/src/data-structures/heap/heap.ts +39 -39
- package/src/data-structures/linked-list/doubly-linked-list.ts +139 -121
- package/src/data-structures/linked-list/singly-linked-list.ts +219 -98
- package/src/data-structures/trie/trie.ts +116 -11
- package/src/index.ts +1 -1
- package/src/types/data-structures/binary-tree/binary-tree.ts +1 -1
- package/src/types/data-structures/binary-tree/bst.ts +3 -2
- package/src/types/data-structures/binary-tree/rb-tree.ts +1 -1
- package/src/types/utils/utils.ts +16 -10
- package/src/utils/utils.ts +4 -2
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +3 -0
- package/test/performance/data-structures/binary-tree/rb-tree.test.ts +4 -1
- package/test/performance/reportor.ts +38 -33
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +12 -12
- package/test/unit/data-structures/binary-tree/bst.test.ts +79 -3
- package/test/unit/data-structures/binary-tree/overall.test.ts +14 -22
- package/test/unit/data-structures/binary-tree/rb-tree.test.ts +100 -1
- package/test/unit/data-structures/trie/trie.test.ts +151 -0
- package/test/unit/utils/utils.test.ts +6 -6
- package/dist/cjs/constants/index.d.ts +0 -4
- package/dist/cjs/constants/index.js +0 -9
- package/dist/cjs/constants/index.js.map +0 -1
- package/dist/mjs/constants/index.d.ts +0 -4
- package/dist/mjs/constants/index.js +0 -6
- package/dist/mjs/constants/index.js.map +0 -1
- package/src/constants/index.ts +0 -4
- package/testToExample.ts +0 -215
package/src/types/utils/utils.ts
CHANGED
|
@@ -7,17 +7,23 @@ export type SpecifyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T,
|
|
|
7
7
|
|
|
8
8
|
export type Any = string | number | bigint | boolean | symbol | undefined | object;
|
|
9
9
|
|
|
10
|
+
export type Arithmetic = number | bigint;
|
|
11
|
+
|
|
10
12
|
export type ComparablePrimitive = number | bigint | string | boolean;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
export interface BaseComparableObject {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ValueComparableObject extends BaseComparableObject {
|
|
19
|
+
valueOf: () => ComparablePrimitive | ValueComparableObject;
|
|
20
|
+
toString?: () => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface StringComparableObject extends BaseComparableObject {
|
|
24
|
+
toString: () => string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ComparableObject = ValueComparableObject | StringComparableObject;
|
|
22
28
|
|
|
23
29
|
export type Comparable = ComparablePrimitive | Date | ComparableObject;
|
package/src/utils/utils.ts
CHANGED
|
@@ -226,7 +226,8 @@ export const roundFixed = (num: number, digit: number = 10) => {
|
|
|
226
226
|
*/
|
|
227
227
|
function isPrimitiveComparable(value: unknown): value is ComparablePrimitive {
|
|
228
228
|
const valueType = typeof value;
|
|
229
|
-
if (valueType === 'number') return
|
|
229
|
+
if (valueType === 'number') return true;
|
|
230
|
+
// if (valueType === 'number') return !Number.isNaN(value);
|
|
230
231
|
return valueType === 'bigint' || valueType === 'string' || valueType === 'boolean';
|
|
231
232
|
}
|
|
232
233
|
|
|
@@ -274,7 +275,8 @@ export function isComparable(value: unknown, isForceObjectComparable = false): v
|
|
|
274
275
|
if (isPrimitiveComparable(value)) return true;
|
|
275
276
|
|
|
276
277
|
if (typeof value !== 'object') return false;
|
|
277
|
-
if (value instanceof Date) return
|
|
278
|
+
if (value instanceof Date) return true;
|
|
279
|
+
// if (value instanceof Date) return !Number.isNaN(value.getTime());
|
|
278
280
|
if (isForceObjectComparable) return true;
|
|
279
281
|
const comparableValue = tryObjectToPrimitive(value);
|
|
280
282
|
if (comparableValue === null || comparableValue === undefined) return false;
|
|
@@ -19,6 +19,9 @@ suite
|
|
|
19
19
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
20
20
|
for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]);
|
|
21
21
|
})
|
|
22
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
23
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.getNode(randomArray[i]);
|
|
24
|
+
})
|
|
22
25
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => {
|
|
23
26
|
const entries = [...avlTree];
|
|
24
27
|
return entries.length === HUNDRED_THOUSAND;
|
|
@@ -23,9 +23,12 @@ suite
|
|
|
23
23
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
24
24
|
for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]);
|
|
25
25
|
})
|
|
26
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
27
|
+
for (let i = 0; i < randomArray.length; i++) rbTree.getNode(randomArray[i]);
|
|
28
|
+
})
|
|
26
29
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode add randomly`, () => {
|
|
27
30
|
rbTreeNodeMode.clear();
|
|
28
|
-
for (let i = 0; i < randomArray.length; i++)
|
|
31
|
+
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.add(randomArray[i]);
|
|
29
32
|
})
|
|
30
33
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => {
|
|
31
34
|
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.get(randomArray[i]);
|
|
@@ -8,6 +8,22 @@ import { PerformanceTest } from './types';
|
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
|
|
10
10
|
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
|
11
|
+
const isOnlyOrdered = true;
|
|
12
|
+
const runOrder = [
|
|
13
|
+
'heap',
|
|
14
|
+
'avl-tree',
|
|
15
|
+
'rb-tree',
|
|
16
|
+
'doubly-linked-list',
|
|
17
|
+
'directed-graph',
|
|
18
|
+
'queue',
|
|
19
|
+
'deque',
|
|
20
|
+
'hash-map',
|
|
21
|
+
'trie',
|
|
22
|
+
'stack'
|
|
23
|
+
// 'singly-linked-list',
|
|
24
|
+
// 'priority-queue',
|
|
25
|
+
// 'binary-tree-overall'
|
|
26
|
+
];
|
|
11
27
|
|
|
12
28
|
const getRelativePath = (file: string) => {
|
|
13
29
|
return path.relative(__dirname, file);
|
|
@@ -80,7 +96,7 @@ const composeReport = () => {
|
|
|
80
96
|
#json-to-html {
|
|
81
97
|
padding: 0 10px 20px;
|
|
82
98
|
}
|
|
83
|
-
|
|
99
|
+
|
|
84
100
|
.json-to-html-label {
|
|
85
101
|
font-size: 2rem;
|
|
86
102
|
margin: 2rem 0 0 3px;
|
|
@@ -92,19 +108,19 @@ const composeReport = () => {
|
|
|
92
108
|
margin-top: 10px;
|
|
93
109
|
font-size: 16px;
|
|
94
110
|
}
|
|
95
|
-
|
|
111
|
+
|
|
96
112
|
.content table th,
|
|
97
113
|
.content table td {
|
|
98
114
|
padding: 8px 12px;
|
|
99
115
|
text-align: left;
|
|
100
116
|
border: 1px solid #ddd;
|
|
101
117
|
}
|
|
102
|
-
|
|
118
|
+
|
|
103
119
|
.content table th {
|
|
104
120
|
background-color: #f2f2f2;
|
|
105
121
|
font-weight: bold;
|
|
106
122
|
}
|
|
107
|
-
|
|
123
|
+
|
|
108
124
|
.content table tr:nth-child(odd) {
|
|
109
125
|
background-color: #ffffff;
|
|
110
126
|
}
|
|
@@ -188,46 +204,35 @@ function replaceMarkdownContent(startMarker: string, endMarker: string, newText:
|
|
|
188
204
|
});
|
|
189
205
|
}
|
|
190
206
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
'hash-map',
|
|
197
|
-
'trie',
|
|
198
|
-
'avl-tree',
|
|
199
|
-
'binary-tree-overall',
|
|
200
|
-
'directed-graph',
|
|
201
|
-
'doubly-linked-list',
|
|
202
|
-
'singly-linked-list',
|
|
203
|
-
'priority-queue',
|
|
204
|
-
'stack'
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
const sortedPerformanceTests = [...performanceTests].sort((a, b) => {
|
|
208
|
-
const indexA = order.indexOf(a.testName);
|
|
209
|
-
const indexB = order.indexOf(b.testName);
|
|
207
|
+
const sortedPerformanceTests = (
|
|
208
|
+
isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests]
|
|
209
|
+
).sort((a, b) => {
|
|
210
|
+
const indexA = runOrder.indexOf(a.testName);
|
|
211
|
+
const indexB = runOrder.indexOf(b.testName);
|
|
210
212
|
|
|
211
|
-
// If both a and b are in the
|
|
213
|
+
// If both a and b are in the runOrder, sort them according to their indices in the runOrder.
|
|
212
214
|
if (indexA !== -1 && indexB !== -1) {
|
|
213
215
|
return indexA - indexB;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
// If there is only 'a' in the
|
|
218
|
+
// If there is only 'a' in the runOrder, then place 'b' in front.
|
|
217
219
|
if (indexA !== -1) {
|
|
218
220
|
return 1;
|
|
219
221
|
}
|
|
220
222
|
|
|
221
|
-
// If only b is in the
|
|
223
|
+
// If only b is in the runOrder, then a should be placed before it.
|
|
222
224
|
if (indexB !== -1) {
|
|
223
225
|
return -1;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
// If neither a nor b are in
|
|
228
|
+
// If neither a nor b are in runOrder, keep their original runOrder
|
|
227
229
|
return 0;
|
|
228
230
|
});
|
|
229
231
|
|
|
230
|
-
console.log(`${GREEN} Found tests${END}: ${
|
|
232
|
+
console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`);
|
|
233
|
+
console.log(
|
|
234
|
+
`${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
|
235
|
+
);
|
|
231
236
|
|
|
232
237
|
sortedPerformanceTests.forEach(item => {
|
|
233
238
|
const { suite, testName, file } = item;
|
|
@@ -245,22 +250,22 @@ sortedPerformanceTests.forEach(item => {
|
|
|
245
250
|
return {
|
|
246
251
|
'test name': benchmark.name,
|
|
247
252
|
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
|
248
|
-
'executions per sec': numberFix(benchmark.hz, 2),
|
|
253
|
+
// 'executions per sec': numberFix(benchmark.hz, 2),
|
|
249
254
|
// 'executed times': numberFix(benchmark.count, 0),
|
|
250
|
-
|
|
255
|
+
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
|
251
256
|
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
|
252
257
|
};
|
|
253
258
|
});
|
|
254
259
|
|
|
255
260
|
report[testName].testName = testName;
|
|
256
|
-
const isDone = completedCount ===
|
|
261
|
+
const isDone = completedCount === sortedPerformanceTests.length;
|
|
257
262
|
runTime = Number(runTime.toFixed(2));
|
|
258
263
|
const isTimeWarn = runTime > 120;
|
|
259
264
|
console.log(
|
|
260
265
|
// `Files: ${GREEN}${testFileCount}${END} `,
|
|
261
266
|
// `Suites: ${GREEN}${performanceTests.length}${END} `,
|
|
262
|
-
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${
|
|
263
|
-
`Time: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
267
|
+
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`,
|
|
268
|
+
`Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
264
269
|
);
|
|
265
270
|
if (isDone) {
|
|
266
271
|
composeReport();
|
|
@@ -736,7 +736,7 @@ describe('AVLTree toEntryFn', () => {
|
|
|
736
736
|
{ obj: { id: 5 } }
|
|
737
737
|
])
|
|
738
738
|
).toThrowError(
|
|
739
|
-
`When comparing object types, a custom
|
|
739
|
+
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
|
740
740
|
);
|
|
741
741
|
});
|
|
742
742
|
|
|
@@ -744,7 +744,7 @@ describe('AVLTree toEntryFn', () => {
|
|
|
744
744
|
const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>(
|
|
745
745
|
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
|
746
746
|
{
|
|
747
|
-
|
|
747
|
+
extractComparable: key => key.obj.id
|
|
748
748
|
}
|
|
749
749
|
);
|
|
750
750
|
|
|
@@ -103,9 +103,9 @@ describe('BinaryTree addMany', () => {
|
|
|
103
103
|
[undefined, 22, 44, 33]
|
|
104
104
|
);
|
|
105
105
|
expect(tree.get(2)).toBe(22);
|
|
106
|
-
expect(tree.get(tree.
|
|
107
|
-
expect(tree.get(tree.
|
|
108
|
-
expect(tree.get(tree.
|
|
106
|
+
expect(tree.get(tree.getNode(3))).toBe(33);
|
|
107
|
+
expect(tree.get(tree.getNode(4))).toBe(44);
|
|
108
|
+
expect(tree.get(tree.getNode(1))).toBe(1);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
it('should addMany undefined and null', () => {
|
|
@@ -349,7 +349,7 @@ describe('BinaryTree', () => {
|
|
|
349
349
|
expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true);
|
|
350
350
|
expect(tree.getNodes(2, false, null)).toEqual([]);
|
|
351
351
|
expect(tree.getNodes(undefined)).toEqual([]);
|
|
352
|
-
expect(tree.getNodes(tree.
|
|
352
|
+
expect(tree.getNodes(tree.getNode(2), false, tree.root)).toEqual([tree.getNode(2)]);
|
|
353
353
|
});
|
|
354
354
|
|
|
355
355
|
describe('should isKey', () => {
|
|
@@ -362,9 +362,9 @@ describe('BinaryTree', () => {
|
|
|
362
362
|
expect(tree.isKey(-Infinity)).toBe(true);
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
-
it('NaN should not be a key', () => {
|
|
366
|
-
|
|
367
|
-
});
|
|
365
|
+
// it('NaN should not be a key', () => {
|
|
366
|
+
// expect(tree.isKey(NaN)).toBe(false);
|
|
367
|
+
// });
|
|
368
368
|
|
|
369
369
|
it('strings should be a key', () => {
|
|
370
370
|
expect(tree.isKey('hello')).toBe(true);
|
|
@@ -400,9 +400,9 @@ describe('BinaryTree', () => {
|
|
|
400
400
|
expect(tree.isKey(new Date('2024-01-01'))).toBe(true);
|
|
401
401
|
});
|
|
402
402
|
|
|
403
|
-
it('invalid Date objects should not be a key', () => {
|
|
404
|
-
|
|
405
|
-
});
|
|
403
|
+
// it('invalid Date objects should not be a key', () => {
|
|
404
|
+
// expect(tree.isKey(new Date('invalid'))).toBe(false);
|
|
405
|
+
// });
|
|
406
406
|
});
|
|
407
407
|
|
|
408
408
|
describe('arrays', () => {
|
|
@@ -1155,8 +1155,8 @@ describe('BinaryTree', () => {
|
|
|
1155
1155
|
tree.add([3, 'B']);
|
|
1156
1156
|
tree.add([7, 'C']);
|
|
1157
1157
|
|
|
1158
|
-
expect(tree.getPathToRoot(
|
|
1159
|
-
expect(tree.getPathToRoot(
|
|
1158
|
+
expect(tree.getPathToRoot(7)).toEqual([7, 5]);
|
|
1159
|
+
expect(tree.getPathToRoot(1)).toEqual([]);
|
|
1160
1160
|
});
|
|
1161
1161
|
|
|
1162
1162
|
it('should check if the tree is perfectly balanced', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BinaryTreeNode, BST, BSTNode } from '../../../../src';
|
|
1
|
+
import { BinaryTreeNode, BST, BSTNode, Range } from '../../../../src';
|
|
2
2
|
import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config';
|
|
3
3
|
|
|
4
4
|
const isDebug = isDebugTest;
|
|
@@ -974,7 +974,7 @@ describe('BST operations test recursively', () => {
|
|
|
974
974
|
|
|
975
975
|
if (isTestStackOverflow) {
|
|
976
976
|
it('should getLeftMost', () => {
|
|
977
|
-
const bst = new BST<number>([], {
|
|
977
|
+
const bst = new BST<number>([], { extractComparable: key => key });
|
|
978
978
|
for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
|
|
979
979
|
|
|
980
980
|
expect(() => {
|
|
@@ -1009,7 +1009,7 @@ describe('BST isBST', function () {
|
|
|
1009
1009
|
|
|
1010
1010
|
it('isBST when variant is Max', () => {
|
|
1011
1011
|
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
|
1012
|
-
|
|
1012
|
+
isReverse: true
|
|
1013
1013
|
});
|
|
1014
1014
|
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
|
1015
1015
|
expect(bst.isBST()).toBe(true);
|
|
@@ -1529,3 +1529,79 @@ describe('BST iterative methods not map mode test', () => {
|
|
|
1529
1529
|
expect(balanced.leaves(node => balanced.get(node?.key))).toEqual(['a', 'f', 'd', 'i']);
|
|
1530
1530
|
});
|
|
1531
1531
|
});
|
|
1532
|
+
|
|
1533
|
+
describe('classic use', () => {
|
|
1534
|
+
// Test case for finding the kth smallest element
|
|
1535
|
+
it('@example Find kth smallest element', () => {
|
|
1536
|
+
// Create a BST with some elements
|
|
1537
|
+
const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
|
|
1538
|
+
const sortedKeys = bst.dfs(node => node.key, 'IN');
|
|
1539
|
+
|
|
1540
|
+
// Helper function to find kth smallest
|
|
1541
|
+
const findKthSmallest = (k: number): number | undefined => {
|
|
1542
|
+
return sortedKeys[k - 1];
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
// Assertions
|
|
1546
|
+
expect(findKthSmallest(1)).toBe(1);
|
|
1547
|
+
expect(findKthSmallest(3)).toBe(4);
|
|
1548
|
+
expect(findKthSmallest(7)).toBe(8);
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
// Test case for finding elements in a given range
|
|
1552
|
+
it('@example Find elements in a range', () => {
|
|
1553
|
+
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
|
1554
|
+
expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]);
|
|
1555
|
+
expect(bst.search(new Range(4, 12))).toEqual([10, 12, 5, 7]);
|
|
1556
|
+
expect(bst.search(new Range(4, 12, true, false))).toEqual([10, 5, 7]);
|
|
1557
|
+
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
|
|
1558
|
+
expect(bst.search(new Range(15, 20, false))).toEqual([18]);
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
// Test case for Huffman coding simulation
|
|
1562
|
+
it('Huffman coding frequency simulation', () => {
|
|
1563
|
+
// Create a BST to simulate Huffman tree
|
|
1564
|
+
const frequencyBST = new BST<string, number>([
|
|
1565
|
+
['a', 5],
|
|
1566
|
+
['b', 9],
|
|
1567
|
+
['c', 12],
|
|
1568
|
+
['d', 13],
|
|
1569
|
+
['e', 16],
|
|
1570
|
+
['f', 45]
|
|
1571
|
+
]);
|
|
1572
|
+
|
|
1573
|
+
// Sort nodes by frequency
|
|
1574
|
+
const sortedFrequencies = frequencyBST.dfs(node => ({ char: node.key, freq: node.value }), 'IN');
|
|
1575
|
+
|
|
1576
|
+
// Build Huffman tree simulation
|
|
1577
|
+
expect(sortedFrequencies[0].char).toBe('a');
|
|
1578
|
+
expect(sortedFrequencies[5].char).toBe('f');
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
// Test case for Lowest Common Ancestor (LCA)
|
|
1582
|
+
it('@example Find lowest common ancestor', () => {
|
|
1583
|
+
const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
|
1584
|
+
|
|
1585
|
+
function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
|
|
1586
|
+
for (const num of arr1) {
|
|
1587
|
+
if (arr2.indexOf(num) !== -1) {
|
|
1588
|
+
return num;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return undefined;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// LCA helper function
|
|
1595
|
+
const findLCA = (num1: number, num2: number): number | undefined => {
|
|
1596
|
+
const path1 = bst.getPathToRoot(num1);
|
|
1597
|
+
const path2 = bst.getPathToRoot(num2);
|
|
1598
|
+
// Find the first common ancestor
|
|
1599
|
+
return findFirstCommon(path1, path2);
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
// Assertions
|
|
1603
|
+
expect(findLCA(3, 10)).toBe(7);
|
|
1604
|
+
expect(findLCA(5, 35)).toBe(15);
|
|
1605
|
+
expect(findLCA(20, 30)).toBe(25);
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
@@ -58,11 +58,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
58
58
|
it('Should clone a BST works fine', () => {
|
|
59
59
|
const bst = new BST<number>([3, 6, 7, 1, 9], {
|
|
60
60
|
iterationType: 'RECURSIVE',
|
|
61
|
-
|
|
62
|
-
if (a > b) return -1;
|
|
63
|
-
if (a < b) return 1;
|
|
64
|
-
return 0;
|
|
65
|
-
}
|
|
61
|
+
isReverse: true
|
|
66
62
|
});
|
|
67
63
|
expect(bst.size).toBe(5);
|
|
68
64
|
expect(bst.root?.key).toBe(6);
|
|
@@ -70,7 +66,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
70
66
|
expect(bst.root?.left?.right?.key).toBe(7);
|
|
71
67
|
expect(bst.root?.right?.key).toBe(3);
|
|
72
68
|
expect(bst.root?.right?.right?.key).toBe(1);
|
|
73
|
-
expect(bst.
|
|
69
|
+
expect(bst.getNode(9)?.right?.key).toBe(7);
|
|
74
70
|
expect(bst.getHeight()).toBe(2);
|
|
75
71
|
expect(bst.has(9)).toBe(true);
|
|
76
72
|
expect(bst.has(7)).toBe(true);
|
|
@@ -81,7 +77,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
81
77
|
expect(bst.root?.left?.key).toBe(9);
|
|
82
78
|
expect(bst.root?.right?.key).toBe(3);
|
|
83
79
|
expect(bst.root?.right?.right?.key).toBe(1);
|
|
84
|
-
expect(bst.
|
|
80
|
+
expect(bst.getNode(6)?.left?.key).toBe(9);
|
|
85
81
|
expect(bst.getHeight()).toBe(2);
|
|
86
82
|
expect(bst.has(9)).toBe(true);
|
|
87
83
|
expect(bst.has(7)).toBe(false);
|
|
@@ -92,7 +88,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
92
88
|
expect(clonedBST.root?.left?.key).toBe(9);
|
|
93
89
|
expect(clonedBST.root?.right?.key).toBe(3);
|
|
94
90
|
expect(clonedBST.root?.right?.right?.key).toBe(1);
|
|
95
|
-
expect(clonedBST.
|
|
91
|
+
expect(clonedBST.getNode(6)?.left?.key).toBe(9);
|
|
96
92
|
expect(clonedBST.getHeight()).toBe(2);
|
|
97
93
|
expect(clonedBST.has(9)).toBe(true);
|
|
98
94
|
expect(clonedBST.has(7)).toBe(false);
|
|
@@ -102,11 +98,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
102
98
|
it('Should clone a AVLTree works fine', () => {
|
|
103
99
|
const avl = new AVLTree<number>([3, 6, 7, 1, 9], {
|
|
104
100
|
iterationType: 'RECURSIVE',
|
|
105
|
-
|
|
106
|
-
if (a > b) return -1;
|
|
107
|
-
if (a < b) return 1;
|
|
108
|
-
return 0;
|
|
109
|
-
}
|
|
101
|
+
isReverse: true
|
|
110
102
|
});
|
|
111
103
|
expect(avl.size).toBe(5);
|
|
112
104
|
avl.add(2);
|
|
@@ -117,7 +109,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
117
109
|
expect(avl.root?.left?.left?.key).toBe(9);
|
|
118
110
|
expect(avl.root?.right?.key).toBe(1);
|
|
119
111
|
expect(avl.root?.right?.left?.key).toBe(2);
|
|
120
|
-
expect(avl.
|
|
112
|
+
expect(avl.getNode(7)?.left?.key).toBe(9);
|
|
121
113
|
expect(avl.getHeight()).toBe(3);
|
|
122
114
|
expect(avl.has(9)).toBe(true);
|
|
123
115
|
expect(avl.has(7)).toBe(true);
|
|
@@ -128,7 +120,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
128
120
|
expect(avl.root?.left?.key).toBe(5);
|
|
129
121
|
expect(avl.root?.right?.key).toBe(1);
|
|
130
122
|
expect(avl.root?.right?.left?.key).toBe(2);
|
|
131
|
-
expect(avl.
|
|
123
|
+
expect(avl.getNode(6)?.left?.key).toBe(undefined);
|
|
132
124
|
expect(avl.getHeight()).toBe(3);
|
|
133
125
|
expect(avl.has(9)).toBe(true);
|
|
134
126
|
expect(avl.has(7)).toBe(false);
|
|
@@ -139,7 +131,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
139
131
|
expect(clonedAVL.root?.left?.key).toBe(5);
|
|
140
132
|
expect(clonedAVL.root?.right?.key).toBe(1);
|
|
141
133
|
expect(clonedAVL.root?.right?.left?.key).toBe(2);
|
|
142
|
-
expect(clonedAVL.
|
|
134
|
+
expect(clonedAVL.getNode(6)?.left?.key).toBe(undefined);
|
|
143
135
|
expect(clonedAVL.getHeight()).toBe(3);
|
|
144
136
|
expect(clonedAVL.has(9)).toBe(true);
|
|
145
137
|
expect(clonedAVL.has(7)).toBe(false);
|
|
@@ -162,7 +154,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
162
154
|
expect(tmm.root?.left?.left?.key).toBe(NaN);
|
|
163
155
|
expect(tmm.root?.right?.key).toBe(7);
|
|
164
156
|
expect(tmm.root?.right?.left?.key).toBe(5);
|
|
165
|
-
expect(tmm.
|
|
157
|
+
expect(tmm.getNode(7)?.left?.key).toBe(5);
|
|
166
158
|
expect(tmm.getHeight()).toBe(3);
|
|
167
159
|
expect(tmm.has(9)).toBe(true);
|
|
168
160
|
expect(tmm.has(7)).toBe(true);
|
|
@@ -174,7 +166,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
174
166
|
expect(tmm.root?.left?.key).toBe(1);
|
|
175
167
|
expect(tmm.root?.right?.key).toBe(9);
|
|
176
168
|
expect(tmm.root?.right?.left?.key).toBe(5);
|
|
177
|
-
expect(tmm.
|
|
169
|
+
expect(tmm.getNode(6)?.left?.key).toBe(NaN);
|
|
178
170
|
expect(tmm.getHeight()).toBe(3);
|
|
179
171
|
expect(tmm.has(9)).toBe(true);
|
|
180
172
|
expect(tmm.has(7)).toBe(false);
|
|
@@ -187,7 +179,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
187
179
|
expect(clonedTMM.root?.left?.key).toBe(1);
|
|
188
180
|
expect(clonedTMM.root?.right?.key).toBe(5);
|
|
189
181
|
expect(clonedTMM.root?.right?.left?.key).toBe(4);
|
|
190
|
-
expect(clonedTMM.
|
|
182
|
+
expect(clonedTMM.getNode(6)?.left?.key).toBe(NaN);
|
|
191
183
|
expect(clonedTMM.getHeight()).toBe(3);
|
|
192
184
|
expect(clonedTMM.has(9)).toBe(true);
|
|
193
185
|
expect(clonedTMM.has(7)).toBe(false);
|
|
@@ -209,7 +201,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
209
201
|
expect(rbTree.root?.left?.left?.key).toBe(NaN);
|
|
210
202
|
expect(rbTree.root?.right?.key).toBe(7);
|
|
211
203
|
expect(rbTree.root?.right?.left?.key).toBe(5);
|
|
212
|
-
expect(rbTree.
|
|
204
|
+
expect(rbTree.getNode(7)?.left?.key).toBe(5);
|
|
213
205
|
expect(rbTree.getHeight()).toBe(3);
|
|
214
206
|
expect(rbTree.has(9)).toBe(true);
|
|
215
207
|
expect(rbTree.has(7)).toBe(true);
|
|
@@ -220,7 +212,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
220
212
|
expect(rbTree.root?.left?.key).toBe(1);
|
|
221
213
|
expect(rbTree.root?.right?.key).toBe(9);
|
|
222
214
|
expect(rbTree.root?.right?.left?.key).toBe(5);
|
|
223
|
-
expect(rbTree.
|
|
215
|
+
expect(rbTree.getNode(6)?.left?.key).toBe(NaN);
|
|
224
216
|
expect(rbTree.getHeight()).toBe(3);
|
|
225
217
|
expect(rbTree.has(9)).toBe(true);
|
|
226
218
|
expect(rbTree.has(7)).toBe(false);
|
|
@@ -232,7 +224,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
232
224
|
expect(clonedRbTree.root?.left?.key).toBe(1);
|
|
233
225
|
expect(clonedRbTree.root?.right?.key).toBe(5);
|
|
234
226
|
expect(clonedRbTree.root?.right?.left?.key).toBe(4);
|
|
235
|
-
expect(clonedRbTree.
|
|
227
|
+
expect(clonedRbTree.getNode(6)?.left?.key).toBe(NaN);
|
|
236
228
|
expect(clonedRbTree.getHeight()).toBe(3);
|
|
237
229
|
expect(clonedRbTree.has(9)).toBe(true);
|
|
238
230
|
expect(clonedRbTree.has(7)).toBe(false);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BinaryTreeNode, BSTNode, RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
|
1
|
+
import { BinaryTreeNode, BSTNode, Range, RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
|
2
2
|
import { getRandomInt, getRandomIntArray, magnitude } from '../../../utils';
|
|
3
3
|
import { OrderedMap } from 'js-sdsl';
|
|
4
4
|
|
|
@@ -819,3 +819,102 @@ describe('RedBlackTree - _deleteFixup', () => {
|
|
|
819
819
|
]);
|
|
820
820
|
});
|
|
821
821
|
});
|
|
822
|
+
|
|
823
|
+
describe('classic use', () => {
|
|
824
|
+
it('Database Index: Add, Search, and Delete Records', () => {
|
|
825
|
+
const dbIndex = new RedBlackTree<number, string>();
|
|
826
|
+
|
|
827
|
+
// Insert records
|
|
828
|
+
dbIndex.add(1, 'Alice');
|
|
829
|
+
dbIndex.add(2, 'Bob');
|
|
830
|
+
dbIndex.add(3, 'Charlie');
|
|
831
|
+
|
|
832
|
+
// Search for records
|
|
833
|
+
expect(dbIndex.get(1)).toBe('Alice');
|
|
834
|
+
expect(dbIndex.get(2)).toBe('Bob');
|
|
835
|
+
expect(dbIndex.get(3)).toBe('Charlie');
|
|
836
|
+
|
|
837
|
+
// Delete a record
|
|
838
|
+
dbIndex.delete(2);
|
|
839
|
+
expect(dbIndex.get(2)).toBeUndefined();
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it('@example Merge 3 sorted datasets', () => {
|
|
843
|
+
const dataset1 = new RedBlackTree<number, string>([
|
|
844
|
+
[1, 'A'],
|
|
845
|
+
[7, 'G']
|
|
846
|
+
]);
|
|
847
|
+
const dataset2 = [
|
|
848
|
+
[2, 'B'],
|
|
849
|
+
[6, 'F']
|
|
850
|
+
];
|
|
851
|
+
const dataset3 = new RedBlackTree<number, string>([
|
|
852
|
+
[3, 'C'],
|
|
853
|
+
[5, 'E'],
|
|
854
|
+
[4, 'D']
|
|
855
|
+
]);
|
|
856
|
+
|
|
857
|
+
// Merge datasets into a single Red-Black Tree
|
|
858
|
+
const merged = new RedBlackTree<number, string>(dataset1);
|
|
859
|
+
merged.addMany(dataset2);
|
|
860
|
+
merged.merge(dataset3);
|
|
861
|
+
|
|
862
|
+
// Verify merged dataset is in sorted order
|
|
863
|
+
expect([...merged.values()]).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// Test case for finding elements in a given range
|
|
867
|
+
it('Find elements in a range', () => {
|
|
868
|
+
const bst = new RedBlackTree<number>([10, 5, 15, 3, 7, 12, 18]);
|
|
869
|
+
expect(bst.search(new Range(5, 10))).toEqual([5, 10, 7]);
|
|
870
|
+
expect(bst.search(new Range(4, 12))).toEqual([5, 10, 12, 7]);
|
|
871
|
+
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
it('Timer List: Manage Timed Tasks', () => {
|
|
875
|
+
const timerList = new RedBlackTree<number, string>(); // Key: Time in ms, Value: Task Name
|
|
876
|
+
|
|
877
|
+
// Schedule tasks
|
|
878
|
+
timerList.add(100, 'Task A');
|
|
879
|
+
timerList.add(200, 'Task B');
|
|
880
|
+
timerList.add(50, 'Task C');
|
|
881
|
+
|
|
882
|
+
// Verify the order of tasks by retrieval
|
|
883
|
+
expect([...timerList.values()]).toEqual(['Task C', 'Task A', 'Task B']); // Sorted by key (time)
|
|
884
|
+
|
|
885
|
+
// Remove the earliest task
|
|
886
|
+
timerList.delete(50);
|
|
887
|
+
expect([...timerList.values()]).toEqual(['Task A', 'Task B']);
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
it('Scheduler: Manage Tasks by Priority', () => {
|
|
891
|
+
const scheduler = new RedBlackTree<number, string>(); // Key: Priority, Value: Task Name
|
|
892
|
+
|
|
893
|
+
// Add tasks with different priorities
|
|
894
|
+
scheduler.add(3, 'Low Priority Task');
|
|
895
|
+
scheduler.add(1, 'High Priority Task');
|
|
896
|
+
scheduler.add(2, 'Medium Priority Task');
|
|
897
|
+
|
|
898
|
+
// Verify the order of tasks by retrieval
|
|
899
|
+
expect([...scheduler.values()]).toEqual(['High Priority Task', 'Medium Priority Task', 'Low Priority Task']);
|
|
900
|
+
|
|
901
|
+
// Remove the highest priority task
|
|
902
|
+
scheduler.delete(1);
|
|
903
|
+
expect([...scheduler.values()]).toEqual(['Medium Priority Task', 'Low Priority Task']);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it('Routing Table: Manage IP Routes', () => {
|
|
907
|
+
const routingTable = new RedBlackTree<number, string>(); // Key: IP Address, Value: Route
|
|
908
|
+
|
|
909
|
+
// Add routes
|
|
910
|
+
routingTable.add(1921680101, 'Route A');
|
|
911
|
+
routingTable.add(1921680102, 'Route B');
|
|
912
|
+
routingTable.add(1921680100, 'Route C');
|
|
913
|
+
|
|
914
|
+
// Search for a specific route
|
|
915
|
+
expect(routingTable.get(1921680101)).toBe('Route A');
|
|
916
|
+
|
|
917
|
+
// Verify all routes in sorted order
|
|
918
|
+
expect([...routingTable.values()]).toEqual(['Route C', 'Route A', 'Route B']);
|
|
919
|
+
});
|
|
920
|
+
});
|