max-priority-queue-typed 2.4.5 → 2.5.0

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 (76) hide show
  1. package/README.md +63 -0
  2. package/dist/cjs/index.cjs +400 -119
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs-legacy/index.cjs +399 -118
  5. package/dist/cjs-legacy/index.cjs.map +1 -1
  6. package/dist/esm/index.mjs +400 -119
  7. package/dist/esm/index.mjs.map +1 -1
  8. package/dist/esm-legacy/index.mjs +399 -118
  9. package/dist/esm-legacy/index.mjs.map +1 -1
  10. package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
  11. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
  12. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
  13. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +429 -78
  14. package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
  15. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +212 -32
  16. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
  17. package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
  18. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
  19. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
  20. package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
  21. package/dist/types/data-structures/graph/directed-graph.d.ts +219 -47
  22. package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
  23. package/dist/types/data-structures/graph/undirected-graph.d.ts +204 -59
  24. package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
  25. package/dist/types/data-structures/heap/heap.d.ts +287 -99
  26. package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
  27. package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
  28. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +286 -44
  29. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
  30. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
  31. package/dist/types/data-structures/matrix/matrix.d.ts +331 -0
  32. package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
  33. package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
  34. package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
  35. package/dist/types/data-structures/queue/deque.d.ts +272 -65
  36. package/dist/types/data-structures/queue/queue.d.ts +211 -42
  37. package/dist/types/data-structures/stack/stack.d.ts +174 -32
  38. package/dist/types/data-structures/trie/trie.d.ts +213 -43
  39. package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
  40. package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
  41. package/dist/umd/max-priority-queue-typed.js +397 -116
  42. package/dist/umd/max-priority-queue-typed.js.map +1 -1
  43. package/dist/umd/max-priority-queue-typed.min.js +1 -1
  44. package/dist/umd/max-priority-queue-typed.min.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/data-structures/base/iterable-element-base.ts +4 -5
  47. package/src/data-structures/binary-tree/avl-tree.ts +134 -51
  48. package/src/data-structures/binary-tree/binary-indexed-tree.ts +302 -247
  49. package/src/data-structures/binary-tree/binary-tree.ts +429 -79
  50. package/src/data-structures/binary-tree/bst.ts +335 -34
  51. package/src/data-structures/binary-tree/red-black-tree.ts +290 -97
  52. package/src/data-structures/binary-tree/segment-tree.ts +372 -248
  53. package/src/data-structures/binary-tree/tree-map.ts +1284 -6
  54. package/src/data-structures/binary-tree/tree-multi-map.ts +1094 -211
  55. package/src/data-structures/binary-tree/tree-multi-set.ts +858 -65
  56. package/src/data-structures/binary-tree/tree-set.ts +1136 -9
  57. package/src/data-structures/graph/directed-graph.ts +219 -47
  58. package/src/data-structures/graph/map-graph.ts +59 -1
  59. package/src/data-structures/graph/undirected-graph.ts +204 -59
  60. package/src/data-structures/hash/hash-map.ts +230 -77
  61. package/src/data-structures/heap/heap.ts +287 -99
  62. package/src/data-structures/heap/max-heap.ts +46 -0
  63. package/src/data-structures/heap/min-heap.ts +59 -0
  64. package/src/data-structures/linked-list/doubly-linked-list.ts +286 -44
  65. package/src/data-structures/linked-list/singly-linked-list.ts +278 -65
  66. package/src/data-structures/linked-list/skip-linked-list.ts +689 -90
  67. package/src/data-structures/matrix/matrix.ts +416 -12
  68. package/src/data-structures/priority-queue/max-priority-queue.ts +57 -0
  69. package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
  70. package/src/data-structures/priority-queue/priority-queue.ts +60 -0
  71. package/src/data-structures/queue/deque.ts +272 -65
  72. package/src/data-structures/queue/queue.ts +211 -42
  73. package/src/data-structures/stack/stack.ts +174 -32
  74. package/src/data-structures/trie/trie.ts +213 -43
  75. package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
  76. package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
@@ -1,171 +1,770 @@
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;
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;
59
+
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);
21
80
  }
81
+ }
22
82
 
23
- if (elements) {
24
- for (const [key, value] of elements) this.add(key, value);
25
- }
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
+ };
26
106
  }
27
107
 
28
- protected _head: SkipListNode<K, V> = new SkipListNode<K, V>(undefined as K, undefined as V, this.maxLevel);
108
+ // ─── Internal state ──────────────────────────────────────────
29
109
 
30
- get head(): SkipListNode<K, V> {
31
- return this._head;
32
- }
110
+ protected _head: SkipListNode<K, V>;
33
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
+ * @example
149
+ * // Check if empty
150
+ * const sl = new SkipList<number, string>();
151
+ * console.log(sl.isEmpty()); // true;
152
+ */
153
+ isEmpty(): boolean {
154
+ return this._size === 0;
65
155
  }
66
156
 
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;
157
+ /**
158
+ * Remove all entries
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+ * @example
168
+ * // Remove all entries
169
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
170
+ * sl.clear();
171
+ * console.log(sl.isEmpty()); // true;
172
+ */
173
+ clear(): void {
174
+ this._head = new SkipListNode<K, V>(undefined as K, undefined as V, this._maxLevel);
175
+ this._level = 0;
176
+ this._size = 0;
177
+ }
178
+
179
+ /**
180
+ * Create independent copy
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+ * @example
190
+ * // Create independent copy
191
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
192
+ * const copy = sl.clone();
193
+ * copy.delete(1);
194
+ * console.log(sl.has(1)); // true;
195
+ */
196
+ clone(): this {
197
+ return new SkipList<K, V, R>(this as Iterable<[K, V | undefined]>, {
198
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
199
+ maxLevel: this._maxLevel,
200
+ probability: this._probability
201
+ }) as this;
202
+ }
203
+
204
+ // ─── Core CRUD ───────────────────────────────────────────────
205
+
206
+ /**
207
+ * Insert or update a key-value pair. Returns `this` for chaining.
208
+ * Unique keys only — if key exists, value is updated in place.
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+ * @example
221
+ * // In-memory sorted key-value store
222
+ * const store = new SkipList<number, string>();
223
+ *
224
+ * store.set(3, 'three');
225
+ * store.set(1, 'one');
226
+ * store.set(5, 'five');
227
+ * store.set(2, 'two');
228
+ *
229
+ * console.log(store.get(3)); // 'three';
230
+ * console.log(store.get(1)); // 'one';
231
+ * console.log(store.get(5)); // 'five';
232
+ *
233
+ * // Update existing key
234
+ * store.set(3, 'THREE');
235
+ * console.log(store.get(3)); // 'THREE';
236
+ */
237
+ set(key: K, value: V): this {
238
+ const cmp = this.#comparator;
239
+ const update = this._findUpdate(key);
240
+
241
+ // If key already exists, update value in place
242
+ const existing = update[0].forward[0];
243
+ if (existing && cmp(existing.key, key) === 0) {
244
+ existing.value = value;
245
+ return this;
246
+ }
71
247
 
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];
248
+ const newLevel = this._randomLevel();
249
+ const newNode = new SkipListNode(key, value, newLevel);
250
+
251
+ if (newLevel > this._level) {
252
+ for (let i = this._level; i < newLevel; i++) {
253
+ update[i] = this._head;
75
254
  }
76
- update[i] = current;
255
+ this._level = newLevel;
77
256
  }
78
257
 
79
- for (let i = 0; i < newNode.forward.length; i++) {
258
+ for (let i = 0; i < newLevel; i++) {
80
259
  newNode.forward[i] = update[i].forward[i];
81
260
  update[i].forward[i] = newNode;
82
261
  }
83
262
 
84
- if (!newNode.forward[0]) {
85
- this._level = Math.max(this.level, newNode.forward.length);
263
+ this._size++;
264
+ return this;
265
+ }
266
+
267
+ /**
268
+ * Get the value for a key, or `undefined` if not found.
269
+ * Overrides base O(n) with O(log n) skip-list search.
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+ * @example
282
+ * // Building a sorted index
283
+ * type Product = { id: number; name: string; price: number };
284
+ * const products: Product[] = [
285
+ * { id: 1, name: 'Widget', price: 25 },
286
+ * { id: 2, name: 'Gadget', price: 50 },
287
+ * { id: 3, name: 'Doohickey', price: 15 }
288
+ * ];
289
+ *
290
+ * const index = new SkipList<number, Product>(products as any, {
291
+ * toEntryFn: (p: any) => [p.price, p]
292
+ * });
293
+ *
294
+ * // Iterate in sorted order by price
295
+ * const names = [...index.values()].map(p => p!.name);
296
+ * console.log(names); // ['Doohickey', 'Widget', 'Gadget'];
297
+ *
298
+ * // Range search: products between $20 and $60
299
+ * const range = index.rangeSearch([20, 60]);
300
+ * console.log(range.map(([, p]) => p!.name)); // ['Widget', 'Gadget'];
301
+ */
302
+ override get(key: K): V | undefined {
303
+ const node = this._findNode(key);
304
+ return node ? node.value : undefined;
305
+ }
306
+
307
+ /**
308
+ * Check if a key exists.
309
+ * Overrides base O(n) with O(log n) skip-list search.
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+ * @example
322
+ * // Check key existence
323
+ * const sl = new SkipList<number, string>([[1, 'a'], [3, 'c'], [5, 'e']]);
324
+ * console.log(sl.has(3)); // true;
325
+ * console.log(sl.has(4)); // false;
326
+ */
327
+ override has(key: K): boolean {
328
+ return this._findNode(key) !== undefined;
329
+ }
330
+
331
+ /**
332
+ * Delete a key. Returns `true` if the key was found and removed.
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+ * @example
345
+ * // Fast lookup with deletion
346
+ * const cache = new SkipList<string, number>();
347
+ *
348
+ * cache.set('alpha', 1);
349
+ * cache.set('beta', 2);
350
+ * cache.set('gamma', 3);
351
+ *
352
+ * console.log(cache.has('beta')); // true;
353
+ * cache.delete('beta');
354
+ * console.log(cache.has('beta')); // false;
355
+ * console.log(cache.size); // 2;
356
+ */
357
+ delete(key: K): boolean {
358
+ const cmp = this.#comparator;
359
+ const update = this._findUpdate(key);
360
+
361
+ const target = update[0].forward[0];
362
+ if (!target || cmp(target.key, key) !== 0) return false;
363
+
364
+ for (let i = 0; i < this._level; i++) {
365
+ if (update[i].forward[i] !== target) break;
366
+ update[i].forward[i] = target.forward[i];
367
+ }
368
+
369
+ while (this._level > 0 && !this._head.forward[this._level - 1]) {
370
+ this._level--;
86
371
  }
372
+
373
+ this._size--;
374
+ return true;
87
375
  }
88
376
 
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];
377
+ // ─── Navigation ──────────────────────────────────────────────
378
+
379
+ /**
380
+ * Returns the first (smallest key) entry, or `undefined` if empty.
381
+
382
+
383
+
384
+
385
+
386
+
387
+
388
+
389
+
390
+
391
+
392
+ * @example
393
+ * // Access the minimum entry
394
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
395
+ * console.log(sl.first()); // [1, 'a'];
396
+ */
397
+ first(): [K, V | undefined] | undefined {
398
+ const node = this._head.forward[0];
399
+ return node ? [node.key, node.value] : undefined;
400
+ }
401
+
402
+ /**
403
+ * Returns the last (largest key) entry, or `undefined` if empty.
404
+
405
+
406
+
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+
415
+ * @example
416
+ * // Access the maximum entry
417
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
418
+ * console.log(sl.last()); // [5, 'e'];
419
+ */
420
+ last(): [K, V | undefined] | undefined {
421
+ let current = this._head;
422
+ for (let i = this._level - 1; i >= 0; i--) {
423
+ while (current.forward[i]) {
424
+ current = current.forward[i]!;
94
425
  }
95
426
  }
427
+ return current === this._head ? undefined : [current.key, current.value];
428
+ }
429
+
430
+ /**
431
+ * Remove and return the first (smallest key) entry.
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+ * @example
441
+ * // Remove and return smallest
442
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
443
+ * console.log(sl.pollFirst()); // [1, 'a'];
444
+ * console.log(sl.size); // 2;
445
+ */
446
+ pollFirst(): [K, V | undefined] | undefined {
447
+ const entry = this.first();
448
+ if (!entry) return undefined;
449
+ this.delete(entry[0]);
450
+ return entry;
451
+ }
452
+
453
+ /**
454
+ * Remove and return the last (largest key) entry.
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+ * @example
464
+ * // Remove and return largest
465
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
466
+ * console.log(sl.pollLast()); // [3, 'c'];
467
+ * console.log(sl.size); // 2;
468
+ */
469
+ pollLast(): [K, V | undefined] | undefined {
470
+ const entry = this.last();
471
+ if (!entry) return undefined;
472
+ this.delete(entry[0]);
473
+ return entry;
474
+ }
96
475
 
97
- current = current.forward[0];
476
+ /**
477
+ * Least entry ≥ key, or `undefined`.
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+
487
+
488
+
489
+ * @example
490
+ * // Least entry ≥ key
491
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
492
+ * console.log(sl.ceiling(15)); // [20, 'b'];
493
+ * console.log(sl.ceiling(20)); // [20, 'b'];
494
+ */
495
+ ceiling(key: K): [K, V | undefined] | undefined {
496
+ const cmp = this.#comparator;
497
+ let current = this._head;
498
+ for (let i = this._level - 1; i >= 0; i--) {
499
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
500
+ current = current.forward[i]!;
501
+ }
502
+ }
503
+ const node = current.forward[0];
504
+ return node ? [node.key, node.value] : undefined;
505
+ }
98
506
 
99
- if (current && current.key === key) {
100
- return current.value;
507
+ /**
508
+ * Greatest entry ≤ key, or `undefined`.
509
+
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+ * @example
521
+ * // Greatest entry ≤ key
522
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
523
+ * console.log(sl.floor(25)); // [20, 'b'];
524
+ * console.log(sl.floor(5)); // undefined;
525
+ */
526
+ floor(key: K): [K, V | undefined] | undefined {
527
+ const cmp = this.#comparator;
528
+ let current = this._head;
529
+ for (let i = this._level - 1; i >= 0; i--) {
530
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
531
+ current = current.forward[i]!;
532
+ }
101
533
  }
534
+ const result = current === this._head ? undefined : current;
102
535
 
536
+ // Check if we're exactly at or before key
537
+ if (result && cmp(result.key, key) <= 0) return [result.key, result.value];
103
538
  return undefined;
104
539
  }
105
540
 
106
- has(key: K): boolean {
107
- return this.get(key) !== undefined;
541
+ /**
542
+ * Least entry strictly > key, or `undefined`.
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
+
551
+ * @example
552
+ * // Strictly greater entry
553
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
554
+ * console.log(sl.higher(15)); // [20, 'b'];
555
+ * console.log(sl.higher(30)); // undefined;
556
+ */
557
+ higher(key: K): [K, V | undefined] | undefined {
558
+ const cmp = this.#comparator;
559
+ let current = this._head;
560
+ for (let i = this._level - 1; i >= 0; i--) {
561
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
562
+ current = current.forward[i]!;
563
+ }
564
+ }
565
+ const node = current.forward[0];
566
+ return node ? [node.key, node.value] : undefined;
108
567
  }
109
568
 
110
- delete(key: K): boolean {
111
- const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
112
- let current = this.head;
569
+ /**
570
+ * Greatest entry strictly < key, or `undefined`.
571
+
572
+
573
+
574
+
575
+
576
+
577
+
578
+
579
+ * @example
580
+ * // Strictly less entry
581
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
582
+ * console.log(sl.lower(25)); // [20, 'b'];
583
+ * console.log(sl.lower(10)); // undefined;
584
+ */
585
+ lower(key: K): [K, V | undefined] | undefined {
586
+ const cmp = this.#comparator;
587
+ let current = this._head;
588
+ let result: SkipListNode<K, V> | undefined;
589
+ for (let i = this._level - 1; i >= 0; i--) {
590
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
591
+ current = current.forward[i]!;
592
+ }
593
+ if (current !== this._head && cmp(current.key, key) < 0) {
594
+ result = current;
595
+ }
596
+ }
597
+ return result ? [result.key, result.value] : undefined;
598
+ }
113
599
 
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];
600
+ /**
601
+ * Returns entries within the given key range.
602
+
603
+
604
+
605
+
606
+
607
+
608
+
609
+
610
+
611
+
612
+
613
+ * @example
614
+ * // Find entries in a range
615
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]);
616
+ * const result = sl.rangeSearch([2, 4]);
617
+ * console.log(result); // [[2, 'b'], [3, 'c'], [4, 'd']];
618
+ */
619
+ rangeSearch(range: [K, K], options: SkipListRangeOptions = {}): Array<[K, V | undefined]> {
620
+ const { lowInclusive = true, highInclusive = true } = options;
621
+ const [low, high] = range;
622
+ const cmp = this.#comparator;
623
+ const out: Array<[K, V | undefined]> = [];
624
+
625
+ // Start from the first node >= low
626
+ let current = this._head;
627
+ for (let i = this._level - 1; i >= 0; i--) {
628
+ while (current.forward[i] && cmp(current.forward[i]!.key, low) < 0) {
629
+ current = current.forward[i]!;
117
630
  }
118
- update[i] = current;
119
631
  }
632
+ current = current.forward[0]!;
120
633
 
121
- current = current.forward[0];
634
+ while (current) {
635
+ const cmpHigh = cmp(current.key, high);
636
+ if (cmpHigh > 0) break;
637
+ if (cmpHigh === 0 && !highInclusive) break;
122
638
 
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];
129
- }
130
- while (this.level > 0 && !this.head.forward[this.level - 1]) {
131
- this._level--;
639
+ const cmpLow = cmp(current.key, low);
640
+ if (cmpLow > 0 || (cmpLow === 0 && lowInclusive)) {
641
+ out.push([current.key, current.value]);
132
642
  }
133
- return true;
643
+
644
+ current = current.forward[0]!;
134
645
  }
135
646
 
136
- return false;
647
+ return out;
137
648
  }
138
649
 
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
- }
650
+ // ─── Functional (overrides) ──────────────────────────────────
651
+
652
+ /**
653
+ * Creates a new SkipList with entries transformed by callback.
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+ * @example
663
+ * // Transform entries
664
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
665
+ * const mapped = sl.map((v, k) => [k, v?.toUpperCase()] as [number, string]);
666
+ * console.log([...mapped.values()]); // ['A', 'B'];
667
+ */
668
+ map<MK, MV>(
669
+ callback: EntryCallback<K, V | undefined, [MK, MV]>,
670
+ options?: SkipListOptions<MK, MV>
671
+ ): SkipList<MK, MV> {
672
+ const out = new SkipList<MK, MV>([], options ?? {});
673
+ let i = 0;
674
+ for (const [k, v] of this) {
675
+ const [nk, nv] = callback(v, k, i++, this);
676
+ out.set(nk, nv);
677
+ }
678
+ return out;
679
+ }
680
+
681
+ /**
682
+ * Creates a new SkipList with entries that pass the predicate.
683
+
684
+
685
+
686
+
687
+
688
+
689
+
690
+
691
+ * @example
692
+ * // Filter entries
693
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
694
+ * const result = sl.filter((v, k) => k > 1);
695
+ * console.log(result.size); // 2;
696
+ */
697
+ filter(
698
+ callbackfn: EntryCallback<K, V | undefined, boolean>,
699
+ thisArg?: unknown
700
+ ): this {
701
+ const out = new SkipList<K, V, R>([], {
702
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
703
+ maxLevel: this._maxLevel,
704
+ probability: this._probability
705
+ });
706
+ let i = 0;
707
+ for (const [k, v] of this) {
708
+ const ok = callbackfn.call(thisArg, v, k, i++, this);
709
+ if (ok) out.set(k, v as V);
145
710
  }
146
- const nextNode = current.forward[0];
147
- return nextNode ? nextNode.value : undefined;
711
+ return out as this;
148
712
  }
149
713
 
150
- lower(key: K): V | undefined {
151
- let current = this.head;
152
- let lastLess = undefined;
714
+ // ─── Iterator (required by IterableEntryBase) ────────────────
153
715
 
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];
716
+ protected _getIterator(): IterableIterator<[K, V | undefined]> {
717
+ const head = this._head;
718
+ return (function* () {
719
+ let node = head.forward[0];
720
+ while (node) {
721
+ yield [node.key, node.value] as [K, V | undefined];
722
+ node = node.forward[0];
157
723
  }
158
- if (current.key < key) {
159
- lastLess = current;
724
+ })();
725
+ }
726
+
727
+ // ─── Internal helpers ────────────────────────────────────────
728
+
729
+ /**
730
+ * Finds the update array (predecessors at each level) for a given key.
731
+ */
732
+ protected _findUpdate(key: K): SkipListNode<K, V>[] {
733
+ const cmp = this.#comparator;
734
+ const update: SkipListNode<K, V>[] = new Array(this._maxLevel).fill(this._head);
735
+ let current = this._head;
736
+
737
+ for (let i = this._level - 1; i >= 0; i--) {
738
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
739
+ current = current.forward[i]!;
160
740
  }
741
+ update[i] = current;
161
742
  }
162
743
 
163
- return lastLess ? lastLess.value : undefined;
744
+ return update;
745
+ }
746
+
747
+ /**
748
+ * Finds the node for a given key, or undefined.
749
+ */
750
+ protected _findNode(key: K): SkipListNode<K, V> | undefined {
751
+ const cmp = this.#comparator;
752
+ let current = this._head;
753
+
754
+ for (let i = this._level - 1; i >= 0; i--) {
755
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
756
+ current = current.forward[i]!;
757
+ }
758
+ }
759
+
760
+ const candidate = current.forward[0];
761
+ if (candidate && cmp(candidate.key, key) === 0) return candidate;
762
+ return undefined;
164
763
  }
165
764
 
166
765
  protected _randomLevel(): number {
167
766
  let level = 1;
168
- while (Math.random() < this.probability && level < this.maxLevel) {
767
+ while (Math.random() < this._probability && level < this._maxLevel) {
169
768
  level++;
170
769
  }
171
770
  return level;