binary-tree-typed 2.4.5 → 2.5.1

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.
Files changed (94) hide show
  1. package/README.md +0 -84
  2. package/dist/cjs/index.cjs +1476 -404
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs-legacy/index.cjs +1473 -401
  5. package/dist/cjs-legacy/index.cjs.map +1 -1
  6. package/dist/esm/index.mjs +1476 -404
  7. package/dist/esm/index.mjs.map +1 -1
  8. package/dist/esm-legacy/index.mjs +1473 -401
  9. package/dist/esm-legacy/index.mjs.map +1 -1
  10. package/dist/types/data-structures/base/index.d.ts +1 -0
  11. package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
  12. package/dist/types/data-structures/base/iterable-entry-base.d.ts +8 -8
  13. package/dist/types/data-structures/base/linear-base.d.ts +3 -3
  14. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +380 -51
  15. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +487 -147
  16. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +956 -80
  17. package/dist/types/data-structures/binary-tree/bst.d.ts +816 -29
  18. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +610 -31
  19. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +326 -135
  20. package/dist/types/data-structures/binary-tree/tree-map.d.ts +3781 -6
  21. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +3607 -201
  22. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +2874 -65
  23. package/dist/types/data-structures/binary-tree/tree-set.d.ts +3528 -6
  24. package/dist/types/data-structures/graph/abstract-graph.d.ts +4 -4
  25. package/dist/types/data-structures/graph/directed-graph.d.ts +429 -47
  26. package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
  27. package/dist/types/data-structures/graph/undirected-graph.d.ts +393 -59
  28. package/dist/types/data-structures/hash/hash-map.d.ts +473 -89
  29. package/dist/types/data-structures/heap/heap.d.ts +581 -99
  30. package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
  31. package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
  32. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +646 -47
  33. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +596 -68
  34. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +793 -12
  35. package/dist/types/data-structures/matrix/matrix.d.ts +499 -0
  36. package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
  37. package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
  38. package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
  39. package/dist/types/data-structures/queue/deque.d.ts +593 -71
  40. package/dist/types/data-structures/queue/queue.d.ts +463 -42
  41. package/dist/types/data-structures/stack/stack.d.ts +384 -32
  42. package/dist/types/data-structures/trie/trie.d.ts +470 -48
  43. package/dist/types/interfaces/graph.d.ts +1 -1
  44. package/dist/types/types/common.d.ts +2 -2
  45. package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
  46. package/dist/types/types/data-structures/heap/heap.d.ts +1 -0
  47. package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
  48. package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +1 -0
  49. package/dist/types/types/utils/validate-type.d.ts +4 -4
  50. package/dist/umd/binary-tree-typed.js +1469 -397
  51. package/dist/umd/binary-tree-typed.js.map +1 -1
  52. package/dist/umd/binary-tree-typed.min.js +5 -5
  53. package/dist/umd/binary-tree-typed.min.js.map +1 -1
  54. package/package.json +2 -2
  55. package/src/data-structures/base/index.ts +1 -0
  56. package/src/data-structures/base/iterable-element-base.ts +4 -5
  57. package/src/data-structures/base/iterable-entry-base.ts +8 -8
  58. package/src/data-structures/base/linear-base.ts +3 -3
  59. package/src/data-structures/binary-tree/avl-tree.ts +386 -51
  60. package/src/data-structures/binary-tree/binary-indexed-tree.ts +596 -247
  61. package/src/data-structures/binary-tree/binary-tree.ts +956 -81
  62. package/src/data-structures/binary-tree/bst.ts +840 -35
  63. package/src/data-structures/binary-tree/red-black-tree.ts +689 -97
  64. package/src/data-structures/binary-tree/segment-tree.ts +498 -249
  65. package/src/data-structures/binary-tree/tree-map.ts +3784 -7
  66. package/src/data-structures/binary-tree/tree-multi-map.ts +3614 -211
  67. package/src/data-structures/binary-tree/tree-multi-set.ts +2874 -65
  68. package/src/data-structures/binary-tree/tree-set.ts +3531 -10
  69. package/src/data-structures/graph/abstract-graph.ts +4 -4
  70. package/src/data-structures/graph/directed-graph.ts +429 -47
  71. package/src/data-structures/graph/map-graph.ts +59 -1
  72. package/src/data-structures/graph/undirected-graph.ts +393 -59
  73. package/src/data-structures/hash/hash-map.ts +476 -92
  74. package/src/data-structures/heap/heap.ts +581 -99
  75. package/src/data-structures/heap/max-heap.ts +46 -0
  76. package/src/data-structures/heap/min-heap.ts +59 -0
  77. package/src/data-structures/linked-list/doubly-linked-list.ts +646 -47
  78. package/src/data-structures/linked-list/singly-linked-list.ts +596 -68
  79. package/src/data-structures/linked-list/skip-linked-list.ts +1067 -90
  80. package/src/data-structures/matrix/matrix.ts +584 -12
  81. package/src/data-structures/priority-queue/max-priority-queue.ts +57 -0
  82. package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
  83. package/src/data-structures/priority-queue/priority-queue.ts +60 -0
  84. package/src/data-structures/queue/deque.ts +592 -70
  85. package/src/data-structures/queue/queue.ts +463 -42
  86. package/src/data-structures/stack/stack.ts +384 -32
  87. package/src/data-structures/trie/trie.ts +470 -48
  88. package/src/interfaces/graph.ts +1 -1
  89. package/src/types/common.ts +2 -2
  90. package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
  91. package/src/types/data-structures/heap/heap.ts +1 -0
  92. package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
  93. package/src/types/data-structures/priority-queue/priority-queue.ts +1 -0
  94. package/src/types/utils/validate-type.ts +4 -4
@@ -1,171 +1,1148 @@
1
- import type { SkipLinkedListOptions } from '../../types';
1
+ /**
2
+ * data-structure-typed
3
+ *
4
+ * @author Pablo Zeng
5
+ * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
+ * @license MIT License
7
+ */
8
+
9
+ import type { Comparator, EntryCallback } from '../../types';
10
+ import { ERR } from '../../common';
11
+ import { IterableEntryBase } from '../base';
2
12
 
3
13
  export class SkipListNode<K, V> {
4
14
  key: K;
5
15
  value: V;
6
- forward: SkipListNode<K, V>[];
16
+ forward: (SkipListNode<K, V> | undefined)[];
7
17
 
8
18
  constructor(key: K, value: V, level: number) {
9
19
  this.key = key;
10
20
  this.value = value;
11
- this.forward = new Array(level);
21
+ this.forward = new Array(level).fill(undefined);
12
22
  }
13
23
  }
14
24
 
15
- export class SkipList<K, V> {
16
- constructor(elements: Iterable<[K, V]> = [], options?: SkipLinkedListOptions) {
17
- if (options) {
18
- const { maxLevel, probability } = options;
19
- if (typeof maxLevel === 'number') this._maxLevel = maxLevel;
20
- if (typeof probability === 'number') this._probability = probability;
21
- }
25
+ export type SkipListOptions<K, V, R = [K, V]> = {
26
+ comparator?: Comparator<K>;
27
+ toEntryFn?: (rawElement: R) => [K, V];
28
+ maxLevel?: number;
29
+ probability?: number;
30
+ };
31
+
32
+ export type SkipListRangeOptions = {
33
+ lowInclusive?: boolean;
34
+ highInclusive?: boolean;
35
+ };
36
+
37
+
38
+ /**
39
+ * SkipList — a probabilistic sorted key-value container.
40
+ *
41
+ * API mirrors TreeMap: users can swap `TreeMap` ↔ `SkipList` with zero code changes.
42
+ * Reference: Java ConcurrentSkipListMap (NavigableMap interface).
43
+ *
44
+ * @example
45
+ * // Display skip list
46
+ * const sl = new SkipList<number, string>([[1, 'a']]);
47
+ * expect(() => sl.print()).not.toThrow();
48
+ */
49
+ export class SkipList<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V | undefined> {
50
+ readonly #comparator: Comparator<K>;
51
+ readonly #isDefaultComparator: boolean;
52
+
53
+ constructor(
54
+ entries: Iterable<R> | Iterable<[K, V | undefined]> = [],
55
+ options: SkipListOptions<K, V, R> = {}
56
+ ) {
57
+ super();
58
+ const { comparator, toEntryFn, maxLevel, probability } = options;
22
59
 
23
- if (elements) {
24
- for (const [key, value] of elements) this.add(key, value);
60
+ if (typeof maxLevel === 'number' && maxLevel > 0) this._maxLevel = maxLevel;
61
+ if (typeof probability === 'number' && probability > 0 && probability < 1) this._probability = probability;
62
+
63
+ this.#isDefaultComparator = comparator === undefined;
64
+ this.#comparator = comparator ?? SkipList.createDefaultComparator<K>();
65
+
66
+ this._head = new SkipListNode<K, V>(undefined as K, undefined as V, this._maxLevel);
67
+
68
+ for (const item of entries) {
69
+ let k: K;
70
+ let v: V | undefined;
71
+ if (toEntryFn) {
72
+ [k, v] = toEntryFn(item as R);
73
+ } else {
74
+ if (!Array.isArray(item) || item.length < 2) {
75
+ throw new TypeError(ERR.invalidEntry('SkipList'));
76
+ }
77
+ [k, v] = item as [K, V];
78
+ }
79
+ this.set(k, v as V);
25
80
  }
26
81
  }
27
82
 
28
- protected _head: SkipListNode<K, V> = new SkipListNode<K, V>(undefined as K, undefined as V, this.maxLevel);
29
-
30
- get head(): SkipListNode<K, V> {
31
- return this._head;
83
+ /**
84
+ * Creates a default comparator supporting number, string, Date, and bigint.
85
+ */
86
+ static createDefaultComparator<K>(): Comparator<K> {
87
+ return (a: K, b: K): number => {
88
+ if (typeof a === 'number' && typeof b === 'number') {
89
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN('SkipList'));
90
+ return a - b;
91
+ }
92
+ if (typeof a === 'string' && typeof b === 'string') {
93
+ return a < b ? -1 : a > b ? 1 : 0;
94
+ }
95
+ if (a instanceof Date && b instanceof Date) {
96
+ const ta = a.getTime(),
97
+ tb = b.getTime();
98
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate('SkipList'));
99
+ return ta - tb;
100
+ }
101
+ if (typeof a === 'bigint' && typeof b === 'bigint') {
102
+ return a < b ? -1 : a > b ? 1 : 0;
103
+ }
104
+ throw new TypeError(ERR.comparatorRequired('SkipList'));
105
+ };
32
106
  }
33
107
 
108
+ // ─── Internal state ──────────────────────────────────────────
109
+
110
+ protected _head: SkipListNode<K, V>;
111
+
34
112
  protected _level: number = 0;
35
113
 
36
- get level(): number {
37
- return this._level;
38
- }
114
+ protected _size: number = 0;
39
115
 
40
116
  protected _maxLevel: number = 16;
41
117
 
118
+ protected _probability: number = 0.5;
119
+
120
+ // ─── Size & lifecycle ────────────────────────────────────────
121
+
122
+ get size(): number {
123
+ return this._size;
124
+ }
125
+
42
126
  get maxLevel(): number {
43
127
  return this._maxLevel;
44
128
  }
45
129
 
46
- protected _probability: number = 0.5;
47
-
48
130
  get probability(): number {
49
131
  return this._probability;
50
132
  }
51
133
 
52
- get first(): V | undefined {
53
- const firstNode = this.head.forward[0];
54
- return firstNode ? firstNode.value : undefined;
134
+ get comparator(): Comparator<K> {
135
+ return this.#comparator;
55
136
  }
56
137
 
57
- get last(): V | undefined {
58
- let current = this.head;
59
- for (let i = this.level - 1; i >= 0; i--) {
60
- while (current.forward[i]) {
61
- current = current.forward[i];
62
- }
63
- }
64
- return current.value;
138
+ /**
139
+ * Check if empty
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
168
+
169
+ * @example
170
+ * // Check if empty
171
+ * const sl = new SkipList<number, string>();
172
+ * console.log(sl.isEmpty()); // true;
173
+ */
174
+ isEmpty(): boolean {
175
+ return this._size === 0;
65
176
  }
66
177
 
67
- add(key: K, value: V): void {
68
- const newNode = new SkipListNode(key, value, this._randomLevel());
69
- const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
70
- let current = this.head;
178
+ /**
179
+ * Remove all entries
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+ * @example
210
+ * // Remove all entries
211
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
212
+ * sl.clear();
213
+ * console.log(sl.isEmpty()); // true;
214
+ */
215
+ clear(): void {
216
+ this._head = new SkipListNode<K, V>(undefined as K, undefined as V, this._maxLevel);
217
+ this._level = 0;
218
+ this._size = 0;
219
+ }
220
+
221
+ /**
222
+ * Create independent copy
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+ * @example
253
+ * // Create independent copy
254
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
255
+ * const copy = sl.clone();
256
+ * copy.delete(1);
257
+ * console.log(sl.has(1)); // true;
258
+ */
259
+ clone(): this {
260
+ return new SkipList<K, V, R>(this as Iterable<[K, V | undefined]>, {
261
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
262
+ maxLevel: this._maxLevel,
263
+ probability: this._probability
264
+ }) as this;
265
+ }
71
266
 
72
- for (let i = this.level - 1; i >= 0; i--) {
73
- while (current.forward[i] && current.forward[i].key < key) {
74
- current = current.forward[i];
267
+ // ─── Core CRUD ───────────────────────────────────────────────
268
+
269
+ /**
270
+ * Insert or update a key-value pair. Returns `this` for chaining.
271
+ * Unique keys only — if key exists, value is updated in place.
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+ * @example
305
+ * // In-memory sorted key-value store
306
+ * const store = new SkipList<number, string>();
307
+ *
308
+ * store.set(3, 'three');
309
+ * store.set(1, 'one');
310
+ * store.set(5, 'five');
311
+ * store.set(2, 'two');
312
+ *
313
+ * console.log(store.get(3)); // 'three';
314
+ * console.log(store.get(1)); // 'one';
315
+ * console.log(store.get(5)); // 'five';
316
+ *
317
+ * // Update existing key
318
+ * store.set(3, 'THREE');
319
+ * console.log(store.get(3)); // 'THREE';
320
+ */
321
+ set(key: K, value: V): this {
322
+ const cmp = this.#comparator;
323
+ const update = this._findUpdate(key);
324
+
325
+ // If key already exists, update value in place
326
+ const existing = update[0].forward[0];
327
+ if (existing && cmp(existing.key, key) === 0) {
328
+ existing.value = value;
329
+ return this;
330
+ }
331
+
332
+ const newLevel = this._randomLevel();
333
+ const newNode = new SkipListNode(key, value, newLevel);
334
+
335
+ if (newLevel > this._level) {
336
+ for (let i = this._level; i < newLevel; i++) {
337
+ update[i] = this._head;
75
338
  }
76
- update[i] = current;
339
+ this._level = newLevel;
77
340
  }
78
341
 
79
- for (let i = 0; i < newNode.forward.length; i++) {
342
+ for (let i = 0; i < newLevel; i++) {
80
343
  newNode.forward[i] = update[i].forward[i];
81
344
  update[i].forward[i] = newNode;
82
345
  }
83
346
 
84
- if (!newNode.forward[0]) {
85
- this._level = Math.max(this.level, newNode.forward.length);
347
+ this._size++;
348
+ return this;
349
+ }
350
+
351
+ /**
352
+ * Get the value for a key, or `undefined` if not found.
353
+ * Overrides base O(n) with O(log n) skip-list search.
354
+
355
+
356
+
357
+
358
+
359
+
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+
386
+ * @example
387
+ * // Building a sorted index
388
+ * type Product = { id: number; name: string; price: number };
389
+ * const products: Product[] = [
390
+ * { id: 1, name: 'Widget', price: 25 },
391
+ * { id: 2, name: 'Gadget', price: 50 },
392
+ * { id: 3, name: 'Doohickey', price: 15 }
393
+ * ];
394
+ *
395
+ * const index = new SkipList<number, Product, Product>(products, {
396
+ * toEntryFn: (p: Product) => [p.price, p]
397
+ * });
398
+ *
399
+ * // Iterate in sorted order by price
400
+ * const names = [...index.values()].map(p => p!.name);
401
+ * console.log(names); // ['Doohickey', 'Widget', 'Gadget'];
402
+ *
403
+ * // Range search: products between $20 and $60
404
+ * const range = index.rangeSearch([20, 60]);
405
+ * console.log(range.map(([, p]) => p!.name)); // ['Widget', 'Gadget'];
406
+ */
407
+ override get(key: K): V | undefined {
408
+ const node = this._findNode(key);
409
+ return node ? node.value : undefined;
410
+ }
411
+
412
+ /**
413
+ * Check if a key exists.
414
+ * Overrides base O(n) with O(log n) skip-list search.
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+ * @example
448
+ * // Check key existence
449
+ * const sl = new SkipList<number, string>([[1, 'a'], [3, 'c'], [5, 'e']]);
450
+ * console.log(sl.has(3)); // true;
451
+ * console.log(sl.has(4)); // false;
452
+ */
453
+ override has(key: K): boolean {
454
+ return this._findNode(key) !== undefined;
455
+ }
456
+
457
+ /**
458
+ * Delete a key. Returns `true` if the key was found and removed.
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+
491
+ * @example
492
+ * // Fast lookup with deletion
493
+ * const cache = new SkipList<string, number>();
494
+ *
495
+ * cache.set('alpha', 1);
496
+ * cache.set('beta', 2);
497
+ * cache.set('gamma', 3);
498
+ *
499
+ * console.log(cache.has('beta')); // true;
500
+ * cache.delete('beta');
501
+ * console.log(cache.has('beta')); // false;
502
+ * console.log(cache.size); // 2;
503
+ */
504
+ delete(key: K): boolean {
505
+ const cmp = this.#comparator;
506
+ const update = this._findUpdate(key);
507
+
508
+ const target = update[0].forward[0];
509
+ if (!target || cmp(target.key, key) !== 0) return false;
510
+
511
+ for (let i = 0; i < this._level; i++) {
512
+ if (update[i].forward[i] !== target) break;
513
+ update[i].forward[i] = target.forward[i];
514
+ }
515
+
516
+ while (this._level > 0 && !this._head.forward[this._level - 1]) {
517
+ this._level--;
86
518
  }
519
+
520
+ this._size--;
521
+ return true;
522
+ }
523
+
524
+ // ─── Navigation ──────────────────────────────────────────────
525
+
526
+ /**
527
+ * Returns the first (smallest key) entry, or `undefined` if empty.
528
+
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
+
551
+
552
+
553
+
554
+
555
+
556
+
557
+
558
+
559
+
560
+ * @example
561
+ * // Access the minimum entry
562
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
563
+ * console.log(sl.first()); // [1, 'a'];
564
+ */
565
+ first(): [K, V | undefined] | undefined {
566
+ const node = this._head.forward[0];
567
+ return node ? [node.key, node.value] : undefined;
87
568
  }
88
569
 
89
- get(key: K): V | undefined {
90
- let current = this.head;
91
- for (let i = this.level - 1; i >= 0; i--) {
92
- while (current.forward[i] && current.forward[i].key < key) {
93
- current = current.forward[i];
570
+ /**
571
+ * Returns the last (largest key) entry, or `undefined` if empty.
572
+
573
+
574
+
575
+
576
+
577
+
578
+
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
604
+ * @example
605
+ * // Access the maximum entry
606
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
607
+ * console.log(sl.last()); // [5, 'e'];
608
+ */
609
+ last(): [K, V | undefined] | undefined {
610
+ let current = this._head;
611
+ for (let i = this._level - 1; i >= 0; i--) {
612
+ while (current.forward[i]) {
613
+ current = current.forward[i]!;
94
614
  }
95
615
  }
616
+ return current === this._head ? undefined : [current.key, current.value];
617
+ }
96
618
 
97
- current = current.forward[0];
619
+ /**
620
+ * Remove and return the first (smallest key) entry.
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+
630
+
631
+
632
+
633
+
634
+
635
+
636
+
637
+
638
+
639
+
640
+
641
+
642
+
643
+
644
+
645
+
646
+
647
+
648
+
649
+
650
+ * @example
651
+ * // Remove and return smallest
652
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
653
+ * console.log(sl.pollFirst()); // [1, 'a'];
654
+ * console.log(sl.size); // 2;
655
+ */
656
+ pollFirst(): [K, V | undefined] | undefined {
657
+ const entry = this.first();
658
+ if (!entry) return undefined;
659
+ this.delete(entry[0]);
660
+ return entry;
661
+ }
662
+
663
+ /**
664
+ * Remove and return the last (largest key) entry.
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+
676
+
677
+
678
+
679
+
680
+
681
+
682
+
683
+
684
+
685
+
686
+
687
+
688
+
689
+
690
+
691
+
692
+
693
+
694
+ * @example
695
+ * // Remove and return largest
696
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
697
+ * console.log(sl.pollLast()); // [3, 'c'];
698
+ * console.log(sl.size); // 2;
699
+ */
700
+ pollLast(): [K, V | undefined] | undefined {
701
+ const entry = this.last();
702
+ if (!entry) return undefined;
703
+ this.delete(entry[0]);
704
+ return entry;
705
+ }
98
706
 
99
- if (current && current.key === key) {
100
- return current.value;
707
+ /**
708
+ * Least entry ≥ key, or `undefined`.
709
+
710
+
711
+
712
+
713
+
714
+
715
+
716
+
717
+
718
+
719
+
720
+
721
+
722
+
723
+
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
740
+
741
+ * @example
742
+ * // Least entry ≥ key
743
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
744
+ * console.log(sl.ceiling(15)); // [20, 'b'];
745
+ * console.log(sl.ceiling(20)); // [20, 'b'];
746
+ */
747
+ ceiling(key: K): [K, V | undefined] | undefined {
748
+ const cmp = this.#comparator;
749
+ let current = this._head;
750
+ for (let i = this._level - 1; i >= 0; i--) {
751
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
752
+ current = current.forward[i]!;
753
+ }
754
+ }
755
+ const node = current.forward[0];
756
+ return node ? [node.key, node.value] : undefined;
757
+ }
758
+
759
+ /**
760
+ * Greatest entry ≤ key, or `undefined`.
761
+
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+ * @example
794
+ * // Greatest entry ≤ key
795
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
796
+ * console.log(sl.floor(25)); // [20, 'b'];
797
+ * console.log(sl.floor(5)); // undefined;
798
+ */
799
+ floor(key: K): [K, V | undefined] | undefined {
800
+ const cmp = this.#comparator;
801
+ let current = this._head;
802
+ for (let i = this._level - 1; i >= 0; i--) {
803
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
804
+ current = current.forward[i]!;
805
+ }
101
806
  }
807
+ const result = current === this._head ? undefined : current;
102
808
 
809
+ // Check if we're exactly at or before key
810
+ if (result && cmp(result.key, key) <= 0) return [result.key, result.value];
103
811
  return undefined;
104
812
  }
105
813
 
106
- has(key: K): boolean {
107
- return this.get(key) !== undefined;
814
+ /**
815
+ * Least entry strictly > key, or `undefined`.
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+
845
+ * @example
846
+ * // Strictly greater entry
847
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
848
+ * console.log(sl.higher(15)); // [20, 'b'];
849
+ * console.log(sl.higher(30)); // undefined;
850
+ */
851
+ higher(key: K): [K, V | undefined] | undefined {
852
+ const cmp = this.#comparator;
853
+ let current = this._head;
854
+ for (let i = this._level - 1; i >= 0; i--) {
855
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
856
+ current = current.forward[i]!;
857
+ }
858
+ }
859
+ const node = current.forward[0];
860
+ return node ? [node.key, node.value] : undefined;
108
861
  }
109
862
 
110
- delete(key: K): boolean {
111
- const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
112
- let current = this.head;
113
-
114
- for (let i = this.level - 1; i >= 0; i--) {
115
- while (current.forward[i] && current.forward[i].key < key) {
116
- current = current.forward[i];
863
+ /**
864
+ * Greatest entry strictly < key, or `undefined`.
865
+
866
+
867
+
868
+
869
+
870
+
871
+
872
+
873
+
874
+
875
+
876
+
877
+
878
+
879
+
880
+
881
+
882
+
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+
891
+
892
+
893
+
894
+ * @example
895
+ * // Strictly less entry
896
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
897
+ * console.log(sl.lower(25)); // [20, 'b'];
898
+ * console.log(sl.lower(10)); // undefined;
899
+ */
900
+ lower(key: K): [K, V | undefined] | undefined {
901
+ const cmp = this.#comparator;
902
+ let current = this._head;
903
+ let result: SkipListNode<K, V> | undefined;
904
+ for (let i = this._level - 1; i >= 0; i--) {
905
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
906
+ current = current.forward[i]!;
907
+ }
908
+ if (current !== this._head && cmp(current.key, key) < 0) {
909
+ result = current;
117
910
  }
118
- update[i] = current;
119
911
  }
912
+ return result ? [result.key, result.value] : undefined;
913
+ }
120
914
 
121
- current = current.forward[0];
915
+ /**
916
+ * Returns entries within the given key range.
917
+
918
+
919
+
920
+
921
+
922
+
923
+
924
+
925
+
926
+
927
+
928
+
929
+
930
+
931
+
932
+
933
+
934
+
935
+
936
+
937
+
938
+
939
+
940
+
941
+
942
+
943
+
944
+
945
+
946
+
947
+
948
+
949
+ * @example
950
+ * // Find entries in a range
951
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]);
952
+ * const result = sl.rangeSearch([2, 4]);
953
+ * console.log(result); // [[2, 'b'], [3, 'c'], [4, 'd']];
954
+ */
955
+ rangeSearch(range: [K, K], options: SkipListRangeOptions = {}): Array<[K, V | undefined]> {
956
+ const { lowInclusive = true, highInclusive = true } = options;
957
+ const [low, high] = range;
958
+ const cmp = this.#comparator;
959
+ const out: Array<[K, V | undefined]> = [];
122
960
 
123
- if (current && current.key === key) {
124
- for (let i = 0; i < this.level; i++) {
125
- if (update[i].forward[i] !== current) {
126
- break;
127
- }
128
- update[i].forward[i] = current.forward[i];
961
+ // Start from the first node >= low
962
+ let current = this._head;
963
+ for (let i = this._level - 1; i >= 0; i--) {
964
+ while (current.forward[i] && cmp(current.forward[i]!.key, low) < 0) {
965
+ current = current.forward[i]!;
129
966
  }
130
- while (this.level > 0 && !this.head.forward[this.level - 1]) {
131
- this._level--;
967
+ }
968
+ current = current.forward[0]!;
969
+
970
+ while (current) {
971
+ const cmpHigh = cmp(current.key, high);
972
+ if (cmpHigh > 0) break;
973
+ if (cmpHigh === 0 && !highInclusive) break;
974
+
975
+ const cmpLow = cmp(current.key, low);
976
+ if (cmpLow > 0 || (cmpLow === 0 && lowInclusive)) {
977
+ out.push([current.key, current.value]);
132
978
  }
133
- return true;
979
+
980
+ current = current.forward[0]!;
134
981
  }
135
982
 
136
- return false;
983
+ return out;
137
984
  }
138
985
 
139
- higher(key: K): V | undefined {
140
- let current = this.head;
141
- for (let i = this.level - 1; i >= 0; i--) {
142
- while (current.forward[i] && current.forward[i].key <= key) {
143
- current = current.forward[i];
144
- }
986
+ // ─── Functional (overrides) ──────────────────────────────────
987
+
988
+ /**
989
+ * Creates a new SkipList with entries transformed by callback.
990
+
991
+
992
+
993
+
994
+
995
+
996
+
997
+
998
+
999
+
1000
+
1001
+
1002
+
1003
+
1004
+
1005
+
1006
+
1007
+
1008
+
1009
+
1010
+
1011
+
1012
+
1013
+
1014
+
1015
+
1016
+
1017
+
1018
+
1019
+ * @example
1020
+ * // Transform entries
1021
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
1022
+ * const mapped = sl.map((v, k) => [k, v?.toUpperCase()] as [number, string]);
1023
+ * console.log([...mapped.values()]); // ['A', 'B'];
1024
+ */
1025
+ map<MK, MV>(
1026
+ callback: EntryCallback<K, V | undefined, [MK, MV]>,
1027
+ options?: SkipListOptions<MK, MV>
1028
+ ): SkipList<MK, MV> {
1029
+ const out = new SkipList<MK, MV>([], options ?? {});
1030
+ let i = 0;
1031
+ for (const [k, v] of this) {
1032
+ const [nk, nv] = callback(v, k, i++, this);
1033
+ out.set(nk, nv);
1034
+ }
1035
+ return out;
1036
+ }
1037
+
1038
+ /**
1039
+ * Creates a new SkipList with entries that pass the predicate.
1040
+
1041
+
1042
+
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+
1056
+
1057
+
1058
+
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+
1065
+
1066
+
1067
+
1068
+
1069
+ * @example
1070
+ * // Filter entries
1071
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
1072
+ * const result = sl.filter((v, k) => k > 1);
1073
+ * console.log(result.size); // 2;
1074
+ */
1075
+ filter(
1076
+ callbackfn: EntryCallback<K, V | undefined, boolean>,
1077
+ thisArg?: unknown
1078
+ ): this {
1079
+ const out = new SkipList<K, V, R>([], {
1080
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
1081
+ maxLevel: this._maxLevel,
1082
+ probability: this._probability
1083
+ });
1084
+ let i = 0;
1085
+ for (const [k, v] of this) {
1086
+ const ok = callbackfn.call(thisArg, v, k, i++, this);
1087
+ if (ok) out.set(k, v as V);
145
1088
  }
146
- const nextNode = current.forward[0];
147
- return nextNode ? nextNode.value : undefined;
1089
+ return out as this;
148
1090
  }
149
1091
 
150
- lower(key: K): V | undefined {
151
- let current = this.head;
152
- let lastLess = undefined;
1092
+ // ─── Iterator (required by IterableEntryBase) ────────────────
153
1093
 
154
- for (let i = this.level - 1; i >= 0; i--) {
155
- while (current.forward[i] && current.forward[i].key < key) {
156
- current = current.forward[i];
1094
+ protected _getIterator(): IterableIterator<[K, V | undefined]> {
1095
+ const head = this._head;
1096
+ return (function* () {
1097
+ let node = head.forward[0];
1098
+ while (node) {
1099
+ yield [node.key, node.value] as [K, V | undefined];
1100
+ node = node.forward[0];
157
1101
  }
158
- if (current.key < key) {
159
- lastLess = current;
1102
+ })();
1103
+ }
1104
+
1105
+ // ─── Internal helpers ────────────────────────────────────────
1106
+
1107
+ /**
1108
+ * Finds the update array (predecessors at each level) for a given key.
1109
+ */
1110
+ protected _findUpdate(key: K): SkipListNode<K, V>[] {
1111
+ const cmp = this.#comparator;
1112
+ const update: SkipListNode<K, V>[] = new Array(this._maxLevel).fill(this._head);
1113
+ let current = this._head;
1114
+
1115
+ for (let i = this._level - 1; i >= 0; i--) {
1116
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
1117
+ current = current.forward[i]!;
160
1118
  }
1119
+ update[i] = current;
161
1120
  }
162
1121
 
163
- return lastLess ? lastLess.value : undefined;
1122
+ return update;
1123
+ }
1124
+
1125
+ /**
1126
+ * Finds the node for a given key, or undefined.
1127
+ */
1128
+ protected _findNode(key: K): SkipListNode<K, V> | undefined {
1129
+ const cmp = this.#comparator;
1130
+ let current = this._head;
1131
+
1132
+ for (let i = this._level - 1; i >= 0; i--) {
1133
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
1134
+ current = current.forward[i]!;
1135
+ }
1136
+ }
1137
+
1138
+ const candidate = current.forward[0];
1139
+ if (candidate && cmp(candidate.key, key) === 0) return candidate;
1140
+ return undefined;
164
1141
  }
165
1142
 
166
1143
  protected _randomLevel(): number {
167
1144
  let level = 1;
168
- while (Math.random() < this.probability && level < this.maxLevel) {
1145
+ while (Math.random() < this._probability && level < this._maxLevel) {
169
1146
  level++;
170
1147
  }
171
1148
  return level;