max-priority-queue-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.
- package/README.md +63 -0
- package/dist/cjs/index.cjs +694 -119
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +693 -118
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +694 -119
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +693 -118
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/base/index.d.ts +1 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
- package/dist/types/data-structures/base/iterable-entry-base.d.ts +8 -8
- package/dist/types/data-structures/base/linear-base.d.ts +3 -3
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +380 -51
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +487 -147
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +956 -80
- package/dist/types/data-structures/binary-tree/bst.d.ts +816 -29
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +610 -31
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +326 -135
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +3781 -6
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +3607 -201
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +2874 -65
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +3528 -6
- package/dist/types/data-structures/graph/abstract-graph.d.ts +4 -4
- package/dist/types/data-structures/graph/directed-graph.d.ts +429 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +393 -59
- package/dist/types/data-structures/hash/hash-map.d.ts +473 -89
- package/dist/types/data-structures/heap/heap.d.ts +581 -99
- package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
- package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +646 -47
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +596 -68
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +793 -12
- package/dist/types/data-structures/matrix/matrix.d.ts +499 -0
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
- package/dist/types/data-structures/queue/deque.d.ts +593 -71
- package/dist/types/data-structures/queue/queue.d.ts +463 -42
- package/dist/types/data-structures/stack/stack.d.ts +384 -32
- package/dist/types/data-structures/trie/trie.d.ts +470 -48
- package/dist/types/interfaces/graph.d.ts +1 -1
- package/dist/types/types/common.d.ts +2 -2
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
- package/dist/types/types/data-structures/heap/heap.d.ts +1 -0
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
- package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +1 -0
- package/dist/types/types/utils/validate-type.d.ts +4 -4
- package/dist/umd/max-priority-queue-typed.js +691 -116
- package/dist/umd/max-priority-queue-typed.js.map +1 -1
- package/dist/umd/max-priority-queue-typed.min.js +1 -1
- package/dist/umd/max-priority-queue-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/base/index.ts +1 -0
- package/src/data-structures/base/iterable-element-base.ts +4 -5
- package/src/data-structures/base/iterable-entry-base.ts +8 -8
- package/src/data-structures/base/linear-base.ts +3 -3
- package/src/data-structures/binary-tree/avl-tree.ts +386 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +596 -247
- package/src/data-structures/binary-tree/binary-tree.ts +956 -81
- package/src/data-structures/binary-tree/bst.ts +840 -35
- package/src/data-structures/binary-tree/red-black-tree.ts +689 -97
- package/src/data-structures/binary-tree/segment-tree.ts +498 -249
- package/src/data-structures/binary-tree/tree-map.ts +3784 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +3614 -211
- package/src/data-structures/binary-tree/tree-multi-set.ts +2874 -65
- package/src/data-structures/binary-tree/tree-set.ts +3531 -10
- package/src/data-structures/graph/abstract-graph.ts +4 -4
- package/src/data-structures/graph/directed-graph.ts +429 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +393 -59
- package/src/data-structures/hash/hash-map.ts +476 -92
- package/src/data-structures/heap/heap.ts +581 -99
- package/src/data-structures/heap/max-heap.ts +46 -0
- package/src/data-structures/heap/min-heap.ts +59 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +646 -47
- package/src/data-structures/linked-list/singly-linked-list.ts +596 -68
- package/src/data-structures/linked-list/skip-linked-list.ts +1067 -90
- package/src/data-structures/matrix/matrix.ts +584 -12
- package/src/data-structures/priority-queue/max-priority-queue.ts +57 -0
- package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
- package/src/data-structures/priority-queue/priority-queue.ts +60 -0
- package/src/data-structures/queue/deque.ts +592 -70
- package/src/data-structures/queue/queue.ts +463 -42
- package/src/data-structures/stack/stack.ts +384 -32
- package/src/data-structures/trie/trie.ts +470 -48
- package/src/interfaces/graph.ts +1 -1
- package/src/types/common.ts +2 -2
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
- package/src/types/data-structures/heap/heap.ts +1 -0
- package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
- package/src/types/data-structures/priority-queue/priority-queue.ts +1 -0
- package/src/types/utils/validate-type.ts +4 -4
|
@@ -1,171 +1,1148 @@
|
|
|
1
|
-
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 (
|
|
24
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
return firstNode ? firstNode.value : undefined;
|
|
134
|
+
get comparator(): Comparator<K> {
|
|
135
|
+
return this.#comparator;
|
|
55
136
|
}
|
|
56
137
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
339
|
+
this._level = newLevel;
|
|
77
340
|
}
|
|
78
341
|
|
|
79
|
-
for (let i = 0; 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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
979
|
+
|
|
980
|
+
current = current.forward[0]!;
|
|
134
981
|
}
|
|
135
982
|
|
|
136
|
-
return
|
|
983
|
+
return out;
|
|
137
984
|
}
|
|
138
985
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
return nextNode ? nextNode.value : undefined;
|
|
1089
|
+
return out as this;
|
|
148
1090
|
}
|
|
149
1091
|
|
|
150
|
-
|
|
151
|
-
let current = this.head;
|
|
152
|
-
let lastLess = undefined;
|
|
1092
|
+
// ─── Iterator (required by IterableEntryBase) ────────────────
|
|
153
1093
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
|
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.
|
|
1145
|
+
while (Math.random() < this._probability && level < this._maxLevel) {
|
|
169
1146
|
level++;
|
|
170
1147
|
}
|
|
171
1148
|
return level;
|