data-structure-typed 2.2.3 → 2.2.5
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 +3 -1
- package/README.md +70 -11
- package/dist/cjs/index.cjs +341 -75
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +343 -75
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +341 -75
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +343 -75
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +2 -3
- package/dist/types/data-structures/binary-tree/bst.d.ts +142 -28
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +5 -5
- package/dist/types/types/data-structures/binary-tree/bst.d.ts +5 -5
- package/dist/umd/data-structure-typed.js +343 -75
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +9 -8
- package/src/data-structures/binary-tree/avl-tree.ts +4 -5
- package/src/data-structures/binary-tree/bst.ts +495 -85
- package/src/data-structures/binary-tree/red-black-tree.ts +1 -2
- package/src/data-structures/binary-tree/tree-counter.ts +5 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +7 -8
- package/src/types/data-structures/binary-tree/bst.ts +5 -5
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/bst.test.ts +459 -8
- package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +1 -1
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +2 -2
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
10
|
BinaryTreeDeleteResult,
|
|
11
|
-
BinaryTreeOptions,
|
|
12
11
|
CRUD,
|
|
13
12
|
EntryCallback,
|
|
14
13
|
FamilyPosition,
|
|
@@ -463,7 +462,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
463
462
|
|
|
464
463
|
override map<MK = K, MV = V, MR = any>(
|
|
465
464
|
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
|
466
|
-
options?: Partial<
|
|
465
|
+
options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
|
|
467
466
|
thisArg?: unknown
|
|
468
467
|
): RedBlackTree<MK, MV, MR> {
|
|
469
468
|
const out = this._createLike<MK, MV, MR>([], options);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
10
|
BinaryTreeDeleteResult,
|
|
11
|
-
BinaryTreeOptions,
|
|
12
11
|
BSTNOptKeyOrNode,
|
|
13
12
|
EntryCallback,
|
|
14
13
|
FamilyPosition,
|
|
@@ -17,7 +16,6 @@ import type {
|
|
|
17
16
|
RBTNColor,
|
|
18
17
|
TreeCounterOptions
|
|
19
18
|
} from '../../types';
|
|
20
|
-
import { BSTOptions } from '../../types';
|
|
21
19
|
import { BSTNode } from './bst';
|
|
22
20
|
import { IBinaryTree } from '../../interfaces';
|
|
23
21
|
import { RedBlackTree } from './red-black-tree';
|
|
@@ -432,7 +430,7 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
432
430
|
*/
|
|
433
431
|
override map<MK = K, MV = V, MR = any>(
|
|
434
432
|
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
|
435
|
-
options?: Partial<
|
|
433
|
+
options?: Partial<TreeCounterOptions<MK, MV, MR>>,
|
|
436
434
|
thisArg?: unknown
|
|
437
435
|
): TreeCounter<MK, MV, MR> {
|
|
438
436
|
const out = this._createLike<MK, MV, MR>([], options);
|
|
@@ -465,10 +463,10 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
465
463
|
* @param [options] - Optional constructor options for the like-kind instance.
|
|
466
464
|
* @returns An empty like-kind instance.
|
|
467
465
|
*/
|
|
468
|
-
protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<
|
|
466
|
+
protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<TreeCounterOptions<TK, TV, TR>>): this {
|
|
469
467
|
const Ctor = this.constructor as unknown as new (
|
|
470
468
|
iter?: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
|
|
471
|
-
opts?:
|
|
469
|
+
opts?: TreeCounterOptions<TK, TV, TR>
|
|
472
470
|
) => this;
|
|
473
471
|
return new Ctor([], { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) }) as unknown as this;
|
|
474
472
|
}
|
|
@@ -485,11 +483,11 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
485
483
|
*/
|
|
486
484
|
protected override _createLike<TK = K, TV = V, TR = R>(
|
|
487
485
|
iter: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR> = [],
|
|
488
|
-
options?: Partial<
|
|
486
|
+
options?: Partial<TreeCounterOptions<TK, TV, TR>>
|
|
489
487
|
): TreeCounter<TK, TV, TR> {
|
|
490
488
|
const Ctor = this.constructor as unknown as new (
|
|
491
489
|
iter?: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
|
|
492
|
-
opts?:
|
|
490
|
+
opts?: TreeCounterOptions<TK, TV, TR>
|
|
493
491
|
) => TreeCounter<TK, TV, TR>;
|
|
494
492
|
return new Ctor(iter as unknown as Iterable<TK | any>, {
|
|
495
493
|
...this._snapshotOptions<TK, TV, TR>(),
|
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
EntryCallback,
|
|
13
13
|
FamilyPosition,
|
|
14
14
|
RBTNColor,
|
|
15
|
-
RedBlackTreeOptions,
|
|
16
15
|
TreeMultiMapOptions
|
|
17
16
|
} from '../../types';
|
|
18
17
|
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
|
|
@@ -476,13 +475,13 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
|
|
|
476
475
|
|
|
477
476
|
override map<MK = K, MVArr extends unknown[] = V[], MR = any>(
|
|
478
477
|
callback: EntryCallback<K, V[] | undefined, [MK, MVArr]>,
|
|
479
|
-
options?: Partial<
|
|
478
|
+
options?: Partial<TreeMultiMapOptions<MK, MVArr, MR>>,
|
|
480
479
|
thisArg?: unknown
|
|
481
480
|
): TreeMultiMap<MK, ElemOf<MVArr>, MR>;
|
|
482
481
|
|
|
483
482
|
override map<MK = K, MV = V[], MR = any>(
|
|
484
483
|
callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
|
|
485
|
-
options?: Partial<
|
|
484
|
+
options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
|
|
486
485
|
thisArg?: unknown
|
|
487
486
|
): RedBlackTree<MK, MV, MR>;
|
|
488
487
|
|
|
@@ -499,7 +498,7 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
|
|
|
499
498
|
*/
|
|
500
499
|
override map<MK, MV, MR extends object>(
|
|
501
500
|
callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
|
|
502
|
-
options?: Partial<
|
|
501
|
+
options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
|
|
503
502
|
thisArg?: unknown
|
|
504
503
|
): RedBlackTree<MK, MV, MR> {
|
|
505
504
|
const out = this._createLike<MK, MV, MR>([], options);
|
|
@@ -517,10 +516,10 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
|
|
|
517
516
|
* @param [options] - Optional constructor options for the like-kind instance.
|
|
518
517
|
* @returns An empty like-kind instance.
|
|
519
518
|
*/
|
|
520
|
-
protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<
|
|
519
|
+
protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<TreeMultiMapOptions<TK, TV, TR>>): this {
|
|
521
520
|
const Ctor = this.constructor as unknown as new (
|
|
522
521
|
iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
|
|
523
|
-
opts?:
|
|
522
|
+
opts?: TreeMultiMapOptions<TK, TV, TR>
|
|
524
523
|
) => RedBlackTree<TK, TV, TR>;
|
|
525
524
|
return new Ctor([], { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) }) as unknown as this;
|
|
526
525
|
}
|
|
@@ -539,11 +538,11 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
|
|
|
539
538
|
iter: Iterable<
|
|
540
539
|
TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR
|
|
541
540
|
> = [],
|
|
542
|
-
options?: Partial<
|
|
541
|
+
options?: Partial<TreeMultiMapOptions<TK, TV, TR>>
|
|
543
542
|
): RedBlackTree<TK, TV, TR> {
|
|
544
543
|
const Ctor = this.constructor as unknown as new (
|
|
545
544
|
iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
|
|
546
|
-
opts?:
|
|
545
|
+
opts?: TreeMultiMapOptions<TK, TV, TR>
|
|
547
546
|
) => RedBlackTree<TK, TV, TR>;
|
|
548
547
|
return new Ctor(iter, { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) });
|
|
549
548
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { BinaryTreeOptions } from './binary-tree';
|
|
2
|
-
import {
|
|
3
|
-
import { OptValue } from '../../common';
|
|
2
|
+
import type { Comparator, OptValue } from '../../common';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
type BSTBaseOptions<K, V, R> = Omit<BinaryTreeOptions<K, V, R>, 'isDuplicate'>;
|
|
5
|
+
|
|
6
|
+
export type BSTOptions<K, V, R> = BSTBaseOptions<K, V, R> & {
|
|
7
|
+
comparator?: Comparator<K>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export type BSTNOptKey<K> = K | undefined;
|
|
@@ -739,7 +739,7 @@ describe('AVLTreeCounter toEntryFn', () => {
|
|
|
739
739
|
{ obj: { id: 5 } }
|
|
740
740
|
])
|
|
741
741
|
).toThrowError(
|
|
742
|
-
`When comparing object
|
|
742
|
+
`When comparing object type keys, a custom comparator must be provided in the constructor's options!`
|
|
743
743
|
);
|
|
744
744
|
});
|
|
745
745
|
|
|
@@ -747,7 +747,7 @@ describe('AVLTreeCounter toEntryFn', () => {
|
|
|
747
747
|
const avlCounter = new AVLTreeCounter<{ obj: { id: number } }, number>(
|
|
748
748
|
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
|
749
749
|
{
|
|
750
|
-
|
|
750
|
+
comparator: (a, b) => a.obj.id - b.obj.id
|
|
751
751
|
}
|
|
752
752
|
);
|
|
753
753
|
|
|
@@ -980,7 +980,7 @@ describe('BST operations test recursively', () => {
|
|
|
980
980
|
|
|
981
981
|
if (isTestStackOverflow) {
|
|
982
982
|
it('should getLeftMost', () => {
|
|
983
|
-
const bst = new BST<number>([]
|
|
983
|
+
const bst = new BST<number>([]);
|
|
984
984
|
for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
|
|
985
985
|
|
|
986
986
|
expect(() => {
|
|
@@ -1015,7 +1015,7 @@ describe('BST isBST', function () {
|
|
|
1015
1015
|
|
|
1016
1016
|
it('isBST when variant is Max', () => {
|
|
1017
1017
|
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
|
1018
|
-
|
|
1018
|
+
comparator: (a, b) => b - a
|
|
1019
1019
|
});
|
|
1020
1020
|
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
|
1021
1021
|
expect(bst.isBST()).toBe(true);
|
|
@@ -1537,19 +1537,16 @@ describe('BST iterative methods not map mode test', () => {
|
|
|
1537
1537
|
});
|
|
1538
1538
|
|
|
1539
1539
|
describe('BST constructor and comparator edge cases', () => {
|
|
1540
|
-
it('should support
|
|
1540
|
+
it('should support comparator', () => {
|
|
1541
1541
|
const bst = new BST<number>([], {
|
|
1542
|
-
|
|
1543
|
-
isReverse: true
|
|
1542
|
+
comparator: (a, b) => b - a
|
|
1544
1543
|
});
|
|
1545
1544
|
bst.add(1);
|
|
1546
1545
|
bst.add(2);
|
|
1547
|
-
expect(bst.isReverse).toBe(true);
|
|
1548
|
-
expect(bst['_specifyComparable']).toBeDefined();
|
|
1549
1546
|
expect([...bst.keys()]).toEqual([2, 1]);
|
|
1550
1547
|
});
|
|
1551
1548
|
|
|
1552
|
-
it('should throw if compare object key without
|
|
1549
|
+
it('should throw if compare object key without comparator', () => {
|
|
1553
1550
|
const bst = new BST<any>();
|
|
1554
1551
|
expect(() => bst.comparator({ a: 1 }, { a: 2 })).toThrow();
|
|
1555
1552
|
});
|
|
@@ -2235,6 +2232,460 @@ describe('BST Advanced Bound Methods Tests', () => {
|
|
|
2235
2232
|
});
|
|
2236
2233
|
});
|
|
2237
2234
|
|
|
2235
|
+
describe('BST Range Query Methods', () => {
|
|
2236
|
+
let bst: BST<number, string>;
|
|
2237
|
+
|
|
2238
|
+
beforeEach(() => {
|
|
2239
|
+
// Create a balanced BST: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20]
|
|
2240
|
+
bst = new BST([10, 5, 15, 3, 7, 13, 17, 1, 4, 6, 9, 11, 14, 16, 19, 20]);
|
|
2241
|
+
});
|
|
2242
|
+
|
|
2243
|
+
describe('ceilingEntry - finds >= key (minimum value >= target)', () => {
|
|
2244
|
+
test('should find ceiling when key exists', () => {
|
|
2245
|
+
const result = bst.ceilingEntry(10);
|
|
2246
|
+
expect(result).toBeDefined();
|
|
2247
|
+
expect(result?.key).toBe(10);
|
|
2248
|
+
});
|
|
2249
|
+
|
|
2250
|
+
test('should find ceiling when key does not exist but higher value exists', () => {
|
|
2251
|
+
const result = bst.ceilingEntry(8);
|
|
2252
|
+
expect(result).toBeDefined();
|
|
2253
|
+
expect(result?.key).toBe(9);
|
|
2254
|
+
});
|
|
2255
|
+
|
|
2256
|
+
test('should return undefined when no ceiling exists (key greater than all)', () => {
|
|
2257
|
+
const result = bst.ceilingEntry(100);
|
|
2258
|
+
expect(result).toBeUndefined();
|
|
2259
|
+
});
|
|
2260
|
+
|
|
2261
|
+
test('should find minimum element as ceiling for key smaller than all', () => {
|
|
2262
|
+
const result = bst.ceilingEntry(-10);
|
|
2263
|
+
expect(result).toBeDefined();
|
|
2264
|
+
expect(result?.key).toBe(1);
|
|
2265
|
+
});
|
|
2266
|
+
|
|
2267
|
+
test('should handle ceiling with node input', () => {
|
|
2268
|
+
const targetNode = bst.getNode(7);
|
|
2269
|
+
expect(targetNode).toBeDefined();
|
|
2270
|
+
const result = bst.ceilingEntry(targetNode!);
|
|
2271
|
+
expect(result?.key).toBe(7);
|
|
2272
|
+
});
|
|
2273
|
+
|
|
2274
|
+
test('should handle ceiling with entry input', () => {
|
|
2275
|
+
const result = bst.ceilingEntry([11, 'test']);
|
|
2276
|
+
expect(result).toBeDefined();
|
|
2277
|
+
expect(result?.key).toBe(11);
|
|
2278
|
+
});
|
|
2279
|
+
|
|
2280
|
+
test('should handle null/undefined inputs', () => {
|
|
2281
|
+
expect(bst.ceilingEntry(null)).toBeUndefined();
|
|
2282
|
+
expect(bst.ceilingEntry(undefined)).toBeUndefined();
|
|
2283
|
+
});
|
|
2284
|
+
|
|
2285
|
+
test('should work with ITERATIVE mode', () => {
|
|
2286
|
+
const result = bst.ceilingEntry(12, 'ITERATIVE');
|
|
2287
|
+
expect(result).toBeDefined();
|
|
2288
|
+
expect(result?.key).toBe(13);
|
|
2289
|
+
});
|
|
2290
|
+
|
|
2291
|
+
test('should work with RECURSIVE mode', () => {
|
|
2292
|
+
const result = bst.ceilingEntry(12, 'RECURSIVE');
|
|
2293
|
+
expect(result).toBeDefined();
|
|
2294
|
+
expect(result?.key).toBe(13);
|
|
2295
|
+
});
|
|
2296
|
+
|
|
2297
|
+
test('should find exact match as ceiling', () => {
|
|
2298
|
+
const result = bst.ceilingEntry(15);
|
|
2299
|
+
expect(result).toBeDefined();
|
|
2300
|
+
expect(result?.key).toBe(15);
|
|
2301
|
+
});
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
describe('higherEntry - finds > key (minimum value > target)', () => {
|
|
2305
|
+
test('should find higher when key exists (exclude exact match)', () => {
|
|
2306
|
+
const result = bst.higherEntry(10);
|
|
2307
|
+
expect(result).toBeDefined();
|
|
2308
|
+
expect(result?.key).toBe(11);
|
|
2309
|
+
expect(result?.key).not.toBe(10);
|
|
2310
|
+
});
|
|
2311
|
+
|
|
2312
|
+
test('should find higher when key does not exist', () => {
|
|
2313
|
+
const result = bst.higherEntry(8);
|
|
2314
|
+
expect(result).toBeDefined();
|
|
2315
|
+
expect(result?.key).toBe(9);
|
|
2316
|
+
});
|
|
2317
|
+
|
|
2318
|
+
test('should return undefined when no higher exists (key >= all)', () => {
|
|
2319
|
+
const result = bst.higherEntry(20);
|
|
2320
|
+
expect(result).toBeUndefined();
|
|
2321
|
+
});
|
|
2322
|
+
|
|
2323
|
+
test('should find minimum element as higher for key < all', () => {
|
|
2324
|
+
const result = bst.higherEntry(-10);
|
|
2325
|
+
expect(result).toBeDefined();
|
|
2326
|
+
expect(result?.key).toBe(1);
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
test('should not return the key itself', () => {
|
|
2330
|
+
const result = bst.higherEntry(7);
|
|
2331
|
+
expect(result?.key).not.toBe(7);
|
|
2332
|
+
expect(result?.key).toBe(9);
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
test('should handle higher with node input', () => {
|
|
2336
|
+
const targetNode = bst.getNode(5);
|
|
2337
|
+
expect(targetNode).toBeDefined();
|
|
2338
|
+
const result = bst.higherEntry(targetNode!);
|
|
2339
|
+
expect(result?.key).toBeGreaterThan(5);
|
|
2340
|
+
expect(result?.key).toBe(6);
|
|
2341
|
+
});
|
|
2342
|
+
|
|
2343
|
+
test('should work with ITERATIVE mode', () => {
|
|
2344
|
+
const result = bst.higherEntry(13, 'ITERATIVE');
|
|
2345
|
+
expect(result).toBeDefined();
|
|
2346
|
+
expect(result?.key).toBe(14);
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
test('should work with RECURSIVE mode', () => {
|
|
2350
|
+
const result = bst.higherEntry(13, 'RECURSIVE');
|
|
2351
|
+
expect(result).toBeDefined();
|
|
2352
|
+
expect(result?.key).toBe(14);
|
|
2353
|
+
});
|
|
2354
|
+
});
|
|
2355
|
+
|
|
2356
|
+
describe('floorEntry - finds <= key (maximum value <= target)', () => {
|
|
2357
|
+
test('should find floor when key exists', () => {
|
|
2358
|
+
const result = bst.floorEntry(10);
|
|
2359
|
+
expect(result).toBeDefined();
|
|
2360
|
+
expect(result?.key).toBe(10);
|
|
2361
|
+
});
|
|
2362
|
+
|
|
2363
|
+
test('should find floor when key does not exist but lower value exists', () => {
|
|
2364
|
+
const result = bst.floorEntry(12);
|
|
2365
|
+
expect(result).toBeDefined();
|
|
2366
|
+
expect(result?.key).toBe(11);
|
|
2367
|
+
});
|
|
2368
|
+
|
|
2369
|
+
test('should return undefined when no floor exists (key less than all)', () => {
|
|
2370
|
+
const result = bst.floorEntry(-10);
|
|
2371
|
+
expect(result).toBeUndefined();
|
|
2372
|
+
});
|
|
2373
|
+
|
|
2374
|
+
test('should find maximum element as floor for key greater than all', () => {
|
|
2375
|
+
const result = bst.floorEntry(100);
|
|
2376
|
+
expect(result).toBeDefined();
|
|
2377
|
+
expect(result?.key).toBe(20);
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
test('should handle floor with node input', () => {
|
|
2381
|
+
const targetNode = bst.getNode(13);
|
|
2382
|
+
expect(targetNode).toBeDefined();
|
|
2383
|
+
const result = bst.floorEntry(targetNode!);
|
|
2384
|
+
expect(result?.key).toBe(13);
|
|
2385
|
+
});
|
|
2386
|
+
|
|
2387
|
+
test('should handle floor with entry input', () => {
|
|
2388
|
+
const result = bst.floorEntry([16, 'test']);
|
|
2389
|
+
expect(result).toBeDefined();
|
|
2390
|
+
expect(result?.key).toBe(16);
|
|
2391
|
+
});
|
|
2392
|
+
|
|
2393
|
+
test('should handle null/undefined inputs', () => {
|
|
2394
|
+
expect(bst.floorEntry(null)).toBeUndefined();
|
|
2395
|
+
expect(bst.floorEntry(undefined)).toBeUndefined();
|
|
2396
|
+
});
|
|
2397
|
+
|
|
2398
|
+
test('should work with ITERATIVE mode', () => {
|
|
2399
|
+
const result = bst.floorEntry(12, 'ITERATIVE');
|
|
2400
|
+
expect(result).toBeDefined();
|
|
2401
|
+
expect(result?.key).toBe(11);
|
|
2402
|
+
});
|
|
2403
|
+
|
|
2404
|
+
test('should work with RECURSIVE mode', () => {
|
|
2405
|
+
const result = bst.floorEntry(12, 'RECURSIVE');
|
|
2406
|
+
expect(result).toBeDefined();
|
|
2407
|
+
expect(result?.key).toBe(11);
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
test('should find exact match as floor', () => {
|
|
2411
|
+
const result = bst.floorEntry(15);
|
|
2412
|
+
expect(result).toBeDefined();
|
|
2413
|
+
expect(result?.key).toBe(15);
|
|
2414
|
+
});
|
|
2415
|
+
|
|
2416
|
+
test('should correctly find floor between two keys', () => {
|
|
2417
|
+
const result = bst.floorEntry(8);
|
|
2418
|
+
expect(result).toBeDefined();
|
|
2419
|
+
expect(result?.key).toBe(7);
|
|
2420
|
+
expect(result?.key).toBeLessThan(8);
|
|
2421
|
+
});
|
|
2422
|
+
});
|
|
2423
|
+
|
|
2424
|
+
describe('lowerEntry - finds < key (maximum value < target)', () => {
|
|
2425
|
+
test('should find lower when key exists (exclude exact match)', () => {
|
|
2426
|
+
const result = bst.lowerEntry(10);
|
|
2427
|
+
expect(result).toBeDefined();
|
|
2428
|
+
expect(result?.key).toBe(9);
|
|
2429
|
+
expect(result?.key).not.toBe(10);
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
test('should find lower when key does not exist', () => {
|
|
2433
|
+
const result = bst.lowerEntry(12);
|
|
2434
|
+
expect(result).toBeDefined();
|
|
2435
|
+
expect(result?.key).toBe(11);
|
|
2436
|
+
});
|
|
2437
|
+
|
|
2438
|
+
test('should return undefined when no lower exists (key <= all)', () => {
|
|
2439
|
+
const result = bst.lowerEntry(1);
|
|
2440
|
+
expect(result).toBeUndefined();
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2443
|
+
test('should find maximum element as lower for key > all', () => {
|
|
2444
|
+
const result = bst.lowerEntry(100);
|
|
2445
|
+
expect(result).toBeDefined();
|
|
2446
|
+
expect(result?.key).toBe(20);
|
|
2447
|
+
});
|
|
2448
|
+
|
|
2449
|
+
test('should not return the key itself', () => {
|
|
2450
|
+
const result = bst.lowerEntry(15);
|
|
2451
|
+
expect(result?.key).not.toBe(15);
|
|
2452
|
+
expect(result?.key).toBe(14);
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2455
|
+
test('should handle lower with node input', () => {
|
|
2456
|
+
const targetNode = bst.getNode(13);
|
|
2457
|
+
expect(targetNode).toBeDefined();
|
|
2458
|
+
const result = bst.lowerEntry(targetNode!);
|
|
2459
|
+
expect(result?.key).toBeLessThan(13);
|
|
2460
|
+
expect(result?.key).toBe(11);
|
|
2461
|
+
});
|
|
2462
|
+
|
|
2463
|
+
test('should handle lower with entry input', () => {
|
|
2464
|
+
const result = bst.lowerEntry([17, 'test']);
|
|
2465
|
+
expect(result).toBeDefined();
|
|
2466
|
+
expect(result?.key).toBe(16);
|
|
2467
|
+
});
|
|
2468
|
+
|
|
2469
|
+
test('should work with ITERATIVE mode', () => {
|
|
2470
|
+
const result = bst.lowerEntry(14, 'ITERATIVE');
|
|
2471
|
+
expect(result).toBeDefined();
|
|
2472
|
+
expect(result?.key).toBe(13);
|
|
2473
|
+
});
|
|
2474
|
+
|
|
2475
|
+
test('should work with RECURSIVE mode', () => {
|
|
2476
|
+
const result = bst.lowerEntry(14, 'RECURSIVE');
|
|
2477
|
+
expect(result).toBeDefined();
|
|
2478
|
+
expect(result?.key).toBe(13);
|
|
2479
|
+
});
|
|
2480
|
+
});
|
|
2481
|
+
|
|
2482
|
+
describe('Edge cases and special scenarios', () => {
|
|
2483
|
+
test('single element tree - ceiling', () => {
|
|
2484
|
+
const singleBst = new BST([5]);
|
|
2485
|
+
expect(singleBst.ceilingEntry(5)?.key).toBe(5);
|
|
2486
|
+
expect(singleBst.ceilingEntry(3)?.key).toBe(5);
|
|
2487
|
+
expect(singleBst.ceilingEntry(7)).toBeUndefined();
|
|
2488
|
+
});
|
|
2489
|
+
|
|
2490
|
+
test('single element tree - higher', () => {
|
|
2491
|
+
const singleBst = new BST([5]);
|
|
2492
|
+
expect(singleBst.higherEntry(5)).toBeUndefined();
|
|
2493
|
+
expect(singleBst.higherEntry(3)?.key).toBe(5);
|
|
2494
|
+
});
|
|
2495
|
+
|
|
2496
|
+
test('single element tree - floor', () => {
|
|
2497
|
+
const singleBst = new BST([5]);
|
|
2498
|
+
expect(singleBst.floorEntry(5)?.key).toBe(5);
|
|
2499
|
+
expect(singleBst.floorEntry(7)?.key).toBe(5);
|
|
2500
|
+
expect(singleBst.floorEntry(3)).toBeUndefined();
|
|
2501
|
+
});
|
|
2502
|
+
|
|
2503
|
+
test('single element tree - lower', () => {
|
|
2504
|
+
const singleBst = new BST([5]);
|
|
2505
|
+
expect(singleBst.lowerEntry(5)).toBeUndefined();
|
|
2506
|
+
expect(singleBst.lowerEntry(7)?.key).toBe(5);
|
|
2507
|
+
});
|
|
2508
|
+
|
|
2509
|
+
test('empty tree handling', () => {
|
|
2510
|
+
const emptyBst = new BST<number, string>();
|
|
2511
|
+
expect(emptyBst.ceilingEntry(5)).toBeUndefined();
|
|
2512
|
+
expect(emptyBst.higherEntry(5)).toBeUndefined();
|
|
2513
|
+
expect(emptyBst.floorEntry(5)).toBeUndefined();
|
|
2514
|
+
expect(emptyBst.lowerEntry(5)).toBeUndefined();
|
|
2515
|
+
});
|
|
2516
|
+
|
|
2517
|
+
test('ceiling and floor of adjacent keys', () => {
|
|
2518
|
+
const ceiling = bst.ceilingEntry(5);
|
|
2519
|
+
const floor = bst.floorEntry(6);
|
|
2520
|
+
expect(ceiling?.key).toBe(5);
|
|
2521
|
+
bst.print()
|
|
2522
|
+
expect(floor?.key).toBe(6);
|
|
2523
|
+
});
|
|
2524
|
+
|
|
2525
|
+
test('higher and lower of adjacent keys', () => {
|
|
2526
|
+
const higher = bst.higherEntry(5);
|
|
2527
|
+
const lower = bst.lowerEntry(6);
|
|
2528
|
+
expect(higher?.key).toBe(6);
|
|
2529
|
+
expect(lower?.key).toBe(5);
|
|
2530
|
+
});
|
|
2531
|
+
});
|
|
2532
|
+
|
|
2533
|
+
describe('Predicate-based search', () => {
|
|
2534
|
+
test('ceiling with predicate function', () => {
|
|
2535
|
+
const result = bst.ceilingEntry((node: BSTNode<number, string>) => node.key >= 10);
|
|
2536
|
+
expect(result).toBeDefined();
|
|
2537
|
+
expect(result?.key).toBeGreaterThanOrEqual(10);
|
|
2538
|
+
});
|
|
2539
|
+
|
|
2540
|
+
test('floor with predicate function', () => {
|
|
2541
|
+
const result = bst.floorEntry((node: BSTNode<number, string>) => node.key <= 15);
|
|
2542
|
+
expect(result).toBeDefined();
|
|
2543
|
+
expect(result?.key).toBeLessThanOrEqual(15);
|
|
2544
|
+
});
|
|
2545
|
+
|
|
2546
|
+
test('higher with predicate function', () => {
|
|
2547
|
+
const result = bst.higherEntry((node: BSTNode<number, string>) => node.key > 10);
|
|
2548
|
+
expect(result).toBeDefined();
|
|
2549
|
+
expect(result?.key).toBeGreaterThan(10);
|
|
2550
|
+
});
|
|
2551
|
+
|
|
2552
|
+
test('lower with predicate function', () => {
|
|
2553
|
+
const result = bst.lowerEntry((node: BSTNode<number, string>) => node.key < 15);
|
|
2554
|
+
expect(result).toBeDefined();
|
|
2555
|
+
expect(result?.key).toBeLessThan(15);
|
|
2556
|
+
});
|
|
2557
|
+
});
|
|
2558
|
+
|
|
2559
|
+
describe('Custom comparator', () => {
|
|
2560
|
+
test('should work with reverse order comparator', () => {
|
|
2561
|
+
const reverseBst = new BST([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20], {
|
|
2562
|
+
comparator: (a: number, b: number) => b - a // reverse order
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2565
|
+
// In reverse order tree: keys are stored in descending order
|
|
2566
|
+
// ceiling (>=) should still work correctly
|
|
2567
|
+
const ceiling = reverseBst.ceilingEntry(10);
|
|
2568
|
+
expect(ceiling).toBeDefined();
|
|
2569
|
+
expect(ceiling?.key).toBeLessThanOrEqual(10);
|
|
2570
|
+
});
|
|
2571
|
+
|
|
2572
|
+
test('should work with string comparator', () => {
|
|
2573
|
+
const stringBst = new BST(
|
|
2574
|
+
[
|
|
2575
|
+
{ name: 'Alice', id: 1 },
|
|
2576
|
+
{ name: 'Bob', id: 2 },
|
|
2577
|
+
{ name: 'Charlie', id: 3 },
|
|
2578
|
+
{ name: 'David', id: 4 },
|
|
2579
|
+
{ name: 'Eve', id: 5 }
|
|
2580
|
+
],
|
|
2581
|
+
{
|
|
2582
|
+
comparator: (a, b) => a.name.localeCompare(b.name)
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
|
|
2586
|
+
const ceiling = stringBst.ceilingEntry({ name: 'Bob', id: 0 });
|
|
2587
|
+
expect(ceiling).toBeDefined();
|
|
2588
|
+
expect(ceiling?.key.name).toBe('Bob');
|
|
2589
|
+
});
|
|
2590
|
+
});
|
|
2591
|
+
|
|
2592
|
+
describe('Performance and correctness validation', () => {
|
|
2593
|
+
test('all range methods return nodes in order', () => {
|
|
2594
|
+
const ceiling = bst.ceilingEntry(10);
|
|
2595
|
+
const higher = bst.higherEntry(10);
|
|
2596
|
+
const floor = bst.floorEntry(10);
|
|
2597
|
+
const lower = bst.lowerEntry(10);
|
|
2598
|
+
|
|
2599
|
+
expect(floor?.key).toBeLessThanOrEqual(10);
|
|
2600
|
+
expect(ceiling?.key).toBeGreaterThanOrEqual(10);
|
|
2601
|
+
expect(higher?.key).toBeGreaterThan(10);
|
|
2602
|
+
expect(lower?.key).toBeLessThan(10);
|
|
2603
|
+
});
|
|
2604
|
+
|
|
2605
|
+
test('range query iteration with ceiling/higher', () => {
|
|
2606
|
+
const results: number[] = [];
|
|
2607
|
+
let node = bst.ceilingEntry(5);
|
|
2608
|
+
let count = 0;
|
|
2609
|
+
while (node && node.key <= 15 && count < 20) {
|
|
2610
|
+
results.push(node.key);
|
|
2611
|
+
node = bst.higherEntry(node.key);
|
|
2612
|
+
count++;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
// Should iterate through nodes 5, 6, 7, ..., 15
|
|
2616
|
+
expect(results.length).toBeGreaterThan(0);
|
|
2617
|
+
expect(results[0]).toBeGreaterThanOrEqual(5);
|
|
2618
|
+
expect(results[results.length - 1]).toBeLessThanOrEqual(15);
|
|
2619
|
+
// Verify ascending order
|
|
2620
|
+
for (let i = 1; i < results.length; i++) {
|
|
2621
|
+
expect(results[i]).toBeGreaterThan(results[i - 1]);
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
|
|
2625
|
+
test('range query iteration with floor/lower', () => {
|
|
2626
|
+
const results: number[] = [];
|
|
2627
|
+
let node = bst.floorEntry(15);
|
|
2628
|
+
let count = 0;
|
|
2629
|
+
while (node && node.key >= 5 && count < 20) {
|
|
2630
|
+
results.push(node.key);
|
|
2631
|
+
node = bst.lowerEntry(node.key);
|
|
2632
|
+
count++;
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// Should iterate through nodes 15, 14, 13, ..., 5
|
|
2636
|
+
expect(results.length).toBeGreaterThan(0);
|
|
2637
|
+
expect(results[0]).toBeLessThanOrEqual(15);
|
|
2638
|
+
expect(results[results.length - 1]).toBeGreaterThanOrEqual(5);
|
|
2639
|
+
// Verify descending order
|
|
2640
|
+
for (let i = 1; i < results.length; i++) {
|
|
2641
|
+
expect(results[i]).toBeLessThan(results[i - 1]);
|
|
2642
|
+
}
|
|
2643
|
+
});
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2646
|
+
describe('Boundary value testing', () => {
|
|
2647
|
+
test('boundary: ceiling at min value', () => {
|
|
2648
|
+
const result = bst.ceilingEntry(1);
|
|
2649
|
+
expect(result?.key).toBe(1);
|
|
2650
|
+
});
|
|
2651
|
+
|
|
2652
|
+
test('boundary: floor at max value', () => {
|
|
2653
|
+
const result = bst.floorEntry(20);
|
|
2654
|
+
expect(result?.key).toBe(20);
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
test('boundary: higher at second-last value', () => {
|
|
2658
|
+
const result = bst.higherEntry(19);
|
|
2659
|
+
expect(result?.key).toBe(20);
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2662
|
+
test('boundary: lower at second value', () => {
|
|
2663
|
+
const result = bst.lowerEntry(3);
|
|
2664
|
+
expect(result?.key).toBe(1);
|
|
2665
|
+
});
|
|
2666
|
+
|
|
2667
|
+
test('boundary: ceiling slightly below min', () => {
|
|
2668
|
+
const result = bst.ceilingEntry(0);
|
|
2669
|
+
expect(result?.key).toBe(1);
|
|
2670
|
+
});
|
|
2671
|
+
|
|
2672
|
+
test('boundary: floor slightly above max', () => {
|
|
2673
|
+
const result = bst.floorEntry(21);
|
|
2674
|
+
expect(result?.key).toBe(20);
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
test('boundary: higher at max (should be undefined)', () => {
|
|
2678
|
+
const result = bst.higherEntry(20);
|
|
2679
|
+
expect(result).toBeUndefined();
|
|
2680
|
+
});
|
|
2681
|
+
|
|
2682
|
+
test('boundary: lower at min (should be undefined)', () => {
|
|
2683
|
+
const result = bst.lowerEntry(1);
|
|
2684
|
+
expect(result).toBeUndefined();
|
|
2685
|
+
});
|
|
2686
|
+
});
|
|
2687
|
+
});
|
|
2688
|
+
|
|
2238
2689
|
describe('classic use', () => {
|
|
2239
2690
|
it('@example basic BST creation and add operation', () => {
|
|
2240
2691
|
// Create a simple BST with numeric keys
|
|
@@ -58,7 +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
|
-
|
|
61
|
+
comparator: (a, b) => b - a
|
|
62
62
|
});
|
|
63
63
|
expect(bst.size).toBe(5);
|
|
64
64
|
expect(bst.root?.key).toBe(6);
|
|
@@ -100,7 +100,7 @@ describe('Overall BinaryTree Test', () => {
|
|
|
100
100
|
it('Should clone a AVLTree works fine', () => {
|
|
101
101
|
const avl = new AVLTree<number>([3, 6, 7, 1, 9], {
|
|
102
102
|
iterationType: 'RECURSIVE',
|
|
103
|
-
|
|
103
|
+
comparator: (a, b) => b - a
|
|
104
104
|
});
|
|
105
105
|
expect(avl.size).toBe(5);
|
|
106
106
|
avl.add(2);
|