max-priority-queue-typed 2.4.4 → 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.
- package/README.md +63 -0
- package/dist/cjs/index.cjs +403 -98
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +402 -97
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +403 -99
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +402 -98
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/common/error.d.ts +23 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +439 -78
- package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +217 -31
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
- package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
- package/dist/types/data-structures/graph/directed-graph.d.ts +220 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +218 -59
- package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
- package/dist/types/data-structures/heap/heap.d.ts +287 -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 +286 -44
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
- package/dist/types/data-structures/matrix/matrix.d.ts +331 -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 +313 -66
- package/dist/types/data-structures/queue/queue.d.ts +211 -42
- package/dist/types/data-structures/stack/stack.d.ts +174 -32
- package/dist/types/data-structures/trie/trie.d.ts +213 -43
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
- package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
- package/dist/umd/max-priority-queue-typed.js +400 -95
- 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/common/error.ts +60 -0
- package/src/common/index.ts +2 -0
- package/src/data-structures/base/iterable-element-base.ts +2 -2
- package/src/data-structures/binary-tree/avl-tree.ts +134 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +303 -247
- package/src/data-structures/binary-tree/binary-tree.ts +542 -121
- package/src/data-structures/binary-tree/bst.ts +346 -37
- package/src/data-structures/binary-tree/red-black-tree.ts +309 -96
- package/src/data-structures/binary-tree/segment-tree.ts +372 -248
- package/src/data-structures/binary-tree/tree-map.ts +1292 -13
- package/src/data-structures/binary-tree/tree-multi-map.ts +1098 -215
- package/src/data-structures/binary-tree/tree-multi-set.ts +863 -69
- package/src/data-structures/binary-tree/tree-set.ts +1143 -15
- package/src/data-structures/graph/abstract-graph.ts +106 -1
- package/src/data-structures/graph/directed-graph.ts +223 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +299 -59
- package/src/data-structures/hash/hash-map.ts +243 -79
- package/src/data-structures/heap/heap.ts +291 -102
- package/src/data-structures/heap/max-heap.ts +48 -3
- package/src/data-structures/heap/min-heap.ts +59 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +286 -44
- package/src/data-structures/linked-list/singly-linked-list.ts +278 -65
- package/src/data-structures/linked-list/skip-linked-list.ts +689 -90
- package/src/data-structures/matrix/matrix.ts +425 -22
- package/src/data-structures/priority-queue/max-priority-queue.ts +59 -3
- 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 +343 -68
- package/src/data-structures/queue/queue.ts +211 -42
- package/src/data-structures/stack/stack.ts +174 -32
- package/src/data-structures/trie/trie.ts +215 -44
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
- package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
- package/src/types/data-structures/queue/deque.ts +7 -0
- package/src/utils/utils.ts +4 -2
|
@@ -1,171 +1,770 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
108
|
+
// ─── Internal state ──────────────────────────────────────────
|
|
29
109
|
|
|
30
|
-
|
|
31
|
-
return this._head;
|
|
32
|
-
}
|
|
110
|
+
protected _head: SkipListNode<K, V>;
|
|
33
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
|
+
* @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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
255
|
+
this._level = newLevel;
|
|
77
256
|
}
|
|
78
257
|
|
|
79
|
-
for (let i = 0; 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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
634
|
+
while (current) {
|
|
635
|
+
const cmpHigh = cmp(current.key, high);
|
|
636
|
+
if (cmpHigh > 0) break;
|
|
637
|
+
if (cmpHigh === 0 && !highInclusive) break;
|
|
122
638
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
643
|
+
|
|
644
|
+
current = current.forward[0]!;
|
|
134
645
|
}
|
|
135
646
|
|
|
136
|
-
return
|
|
647
|
+
return out;
|
|
137
648
|
}
|
|
138
649
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
return nextNode ? nextNode.value : undefined;
|
|
711
|
+
return out as this;
|
|
148
712
|
}
|
|
149
713
|
|
|
150
|
-
|
|
151
|
-
let current = this.head;
|
|
152
|
-
let lastLess = undefined;
|
|
714
|
+
// ─── Iterator (required by IterableEntryBase) ────────────────
|
|
153
715
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
|
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.
|
|
767
|
+
while (Math.random() < this._probability && level < this._maxLevel) {
|
|
169
768
|
level++;
|
|
170
769
|
}
|
|
171
770
|
return level;
|