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