linked-list-typed 2.4.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -50
- package/dist/cjs/index.cjs +1538 -294
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +1544 -291
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +1538 -294
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +1544 -291
- package/dist/esm-legacy/index.mjs.map +1 -1
- 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 +429 -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 +212 -32
- 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/directed-graph.d.ts +219 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +204 -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 +272 -65
- 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/umd/linked-list-typed.js +1541 -289
- package/dist/umd/linked-list-typed.js.map +1 -1
- package/dist/umd/linked-list-typed.min.js +1 -1
- package/dist/umd/linked-list-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/base/iterable-element-base.ts +4 -5
- package/src/data-structures/binary-tree/avl-tree.ts +134 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +302 -247
- package/src/data-structures/binary-tree/binary-tree.ts +429 -79
- package/src/data-structures/binary-tree/bst.ts +335 -34
- package/src/data-structures/binary-tree/red-black-tree.ts +290 -97
- package/src/data-structures/binary-tree/segment-tree.ts +372 -248
- package/src/data-structures/binary-tree/tree-map.ts +1284 -6
- package/src/data-structures/binary-tree/tree-multi-map.ts +1094 -211
- package/src/data-structures/binary-tree/tree-multi-set.ts +858 -65
- package/src/data-structures/binary-tree/tree-set.ts +1136 -9
- package/src/data-structures/graph/directed-graph.ts +219 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +204 -59
- package/src/data-structures/hash/hash-map.ts +230 -77
- package/src/data-structures/heap/heap.ts +287 -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 +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 +416 -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 +272 -65
- 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 +213 -43
- 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/dist/esm/index.mjs
CHANGED
|
@@ -1,55 +1,6 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
-
// src/common/error.ts
|
|
5
|
-
var ERR = {
|
|
6
|
-
// Range / index
|
|
7
|
-
indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"),
|
|
8
|
-
invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"),
|
|
9
|
-
// Type / argument
|
|
10
|
-
invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"),
|
|
11
|
-
comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"),
|
|
12
|
-
invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"),
|
|
13
|
-
notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"),
|
|
14
|
-
invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"),
|
|
15
|
-
invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"),
|
|
16
|
-
invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"),
|
|
17
|
-
reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"),
|
|
18
|
-
callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"),
|
|
19
|
-
// State / operation
|
|
20
|
-
invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"),
|
|
21
|
-
// Matrix
|
|
22
|
-
matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"),
|
|
23
|
-
matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"),
|
|
24
|
-
matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"),
|
|
25
|
-
matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"),
|
|
26
|
-
matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch")
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// src/common/index.ts
|
|
30
|
-
var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
|
|
31
|
-
DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
|
|
32
|
-
DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
|
|
33
|
-
return DFSOperation2;
|
|
34
|
-
})(DFSOperation || {});
|
|
35
|
-
var Range = class {
|
|
36
|
-
constructor(low, high, includeLow = true, includeHigh = true) {
|
|
37
|
-
this.low = low;
|
|
38
|
-
this.high = high;
|
|
39
|
-
this.includeLow = includeLow;
|
|
40
|
-
this.includeHigh = includeHigh;
|
|
41
|
-
}
|
|
42
|
-
static {
|
|
43
|
-
__name(this, "Range");
|
|
44
|
-
}
|
|
45
|
-
// Determine whether a key is within the range
|
|
46
|
-
isInRange(key, comparator) {
|
|
47
|
-
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
|
|
48
|
-
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
|
|
49
|
-
return lowCheck && highCheck;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
4
|
// src/data-structures/base/iterable-element-base.ts
|
|
54
5
|
var IterableElementBase = class {
|
|
55
6
|
static {
|
|
@@ -68,7 +19,7 @@ var IterableElementBase = class {
|
|
|
68
19
|
if (options) {
|
|
69
20
|
const { toElementFn } = options;
|
|
70
21
|
if (typeof toElementFn === "function") this._toElementFn = toElementFn;
|
|
71
|
-
else if (toElementFn) throw new TypeError(
|
|
22
|
+
else if (toElementFn) throw new TypeError("toElementFn must be a function type");
|
|
72
23
|
}
|
|
73
24
|
}
|
|
74
25
|
/**
|
|
@@ -231,7 +182,7 @@ var IterableElementBase = class {
|
|
|
231
182
|
acc = initialValue;
|
|
232
183
|
} else {
|
|
233
184
|
const first = iter.next();
|
|
234
|
-
if (first.done) throw new TypeError(
|
|
185
|
+
if (first.done) throw new TypeError("Reduce of empty structure with no initial value");
|
|
235
186
|
acc = first.value;
|
|
236
187
|
index = 1;
|
|
237
188
|
}
|
|
@@ -772,11 +723,37 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
772
723
|
return list;
|
|
773
724
|
}
|
|
774
725
|
/**
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
726
|
+
* Append an element/node to the tail.
|
|
727
|
+
* @remarks Time O(1), Space O(1)
|
|
728
|
+
* @param elementOrNode - Element or node to append.
|
|
729
|
+
* @returns True when appended.
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
* @example
|
|
742
|
+
* // basic SinglyLinkedList creation and push operation
|
|
743
|
+
* // Create a simple SinglyLinkedList with initial values
|
|
744
|
+
* const list = new SinglyLinkedList([1, 2, 3, 4, 5]);
|
|
745
|
+
*
|
|
746
|
+
* // Verify the list maintains insertion order
|
|
747
|
+
* console.log([...list]); // [1, 2, 3, 4, 5];
|
|
748
|
+
*
|
|
749
|
+
* // Check length
|
|
750
|
+
* console.log(list.length); // 5;
|
|
751
|
+
*
|
|
752
|
+
* // Push a new element to the end
|
|
753
|
+
* list.push(6);
|
|
754
|
+
* console.log(list.length); // 6;
|
|
755
|
+
* console.log([...list]); // [1, 2, 3, 4, 5, 6];
|
|
756
|
+
*/
|
|
780
757
|
push(elementOrNode) {
|
|
781
758
|
const newNode = this._ensureNode(elementOrNode);
|
|
782
759
|
if (!this.head) {
|
|
@@ -790,10 +767,36 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
790
767
|
return true;
|
|
791
768
|
}
|
|
792
769
|
/**
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
770
|
+
* Remove and return the tail element.
|
|
771
|
+
* @remarks Time O(N), Space O(1)
|
|
772
|
+
* @returns Removed element or undefined.
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
* @example
|
|
785
|
+
* // SinglyLinkedList pop and shift operations
|
|
786
|
+
* const list = new SinglyLinkedList<number>([10, 20, 30, 40, 50]);
|
|
787
|
+
*
|
|
788
|
+
* // Pop removes from the end
|
|
789
|
+
* const last = list.pop();
|
|
790
|
+
* console.log(last); // 50;
|
|
791
|
+
*
|
|
792
|
+
* // Shift removes from the beginning
|
|
793
|
+
* const first = list.shift();
|
|
794
|
+
* console.log(first); // 10;
|
|
795
|
+
*
|
|
796
|
+
* // Verify remaining elements
|
|
797
|
+
* console.log([...list]); // [20, 30, 40];
|
|
798
|
+
* console.log(list.length); // 3;
|
|
799
|
+
*/
|
|
797
800
|
pop() {
|
|
798
801
|
if (!this.head) return void 0;
|
|
799
802
|
if (this.head === this.tail) {
|
|
@@ -812,10 +815,26 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
812
815
|
return value;
|
|
813
816
|
}
|
|
814
817
|
/**
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
818
|
+
* Remove and return the head element.
|
|
819
|
+
* @remarks Time O(1), Space O(1)
|
|
820
|
+
* @returns Removed element or undefined.
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
* @example
|
|
833
|
+
* // Remove from the front
|
|
834
|
+
* const list = new SinglyLinkedList<number>([10, 20, 30]);
|
|
835
|
+
* console.log(list.shift()); // 10;
|
|
836
|
+
* console.log(list.length); // 2;
|
|
837
|
+
*/
|
|
819
838
|
shift() {
|
|
820
839
|
if (!this.head) return void 0;
|
|
821
840
|
const removed = this.head;
|
|
@@ -825,11 +844,42 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
825
844
|
return removed.value;
|
|
826
845
|
}
|
|
827
846
|
/**
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
847
|
+
* Prepend an element/node to the head.
|
|
848
|
+
* @remarks Time O(1), Space O(1)
|
|
849
|
+
* @param elementOrNode - Element or node to prepend.
|
|
850
|
+
* @returns True when prepended.
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
* @example
|
|
863
|
+
* // SinglyLinkedList unshift and forward traversal
|
|
864
|
+
* const list = new SinglyLinkedList<number>([20, 30, 40]);
|
|
865
|
+
*
|
|
866
|
+
* // Unshift adds to the beginning
|
|
867
|
+
* list.unshift(10);
|
|
868
|
+
* console.log([...list]); // [10, 20, 30, 40];
|
|
869
|
+
*
|
|
870
|
+
* // Access elements (forward traversal only for singly linked)
|
|
871
|
+
* const second = list.at(1);
|
|
872
|
+
* console.log(second); // 20;
|
|
873
|
+
*
|
|
874
|
+
* // SinglyLinkedList allows forward iteration only
|
|
875
|
+
* const elements: number[] = [];
|
|
876
|
+
* for (const item of list) {
|
|
877
|
+
* elements.push(item);
|
|
878
|
+
* }
|
|
879
|
+
* console.log(elements); // [10, 20, 30, 40];
|
|
880
|
+
*
|
|
881
|
+
* console.log(list.length); // 4;
|
|
882
|
+
*/
|
|
833
883
|
unshift(elementOrNode) {
|
|
834
884
|
const newNode = this._ensureNode(elementOrNode);
|
|
835
885
|
if (!this.head) {
|
|
@@ -885,11 +935,28 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
885
935
|
return void 0;
|
|
886
936
|
}
|
|
887
937
|
/**
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
938
|
+
* Get the element at a given index.
|
|
939
|
+
* @remarks Time O(N), Space O(1)
|
|
940
|
+
* @param index - Zero-based index.
|
|
941
|
+
* @returns Element or undefined.
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
* @example
|
|
954
|
+
* // Access element by index
|
|
955
|
+
* const list = new SinglyLinkedList<string>(['a', 'b', 'c', 'd']);
|
|
956
|
+
* console.log(list.at(0)); // 'a';
|
|
957
|
+
* console.log(list.at(2)); // 'c';
|
|
958
|
+
* console.log(list.at(3)); // 'd';
|
|
959
|
+
*/
|
|
893
960
|
at(index) {
|
|
894
961
|
if (index < 0 || index >= this._length) return void 0;
|
|
895
962
|
let current = this.head;
|
|
@@ -906,11 +973,23 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
906
973
|
return elementNodeOrPredicate instanceof SinglyLinkedListNode;
|
|
907
974
|
}
|
|
908
975
|
/**
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
976
|
+
* Get the node reference at a given index.
|
|
977
|
+
* @remarks Time O(N), Space O(1)
|
|
978
|
+
* @param index - Zero-based index.
|
|
979
|
+
* @returns Node or undefined.
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
* @example
|
|
989
|
+
* // Get node at index
|
|
990
|
+
* const list = new SinglyLinkedList<string>(['a', 'b', 'c']);
|
|
991
|
+
* console.log(list.getNodeAt(1)?.value); // 'b';
|
|
992
|
+
*/
|
|
914
993
|
getNodeAt(index) {
|
|
915
994
|
if (index < 0 || index >= this._length) return void 0;
|
|
916
995
|
let current = this.head;
|
|
@@ -918,11 +997,24 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
918
997
|
return current;
|
|
919
998
|
}
|
|
920
999
|
/**
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1000
|
+
* Delete the element at an index.
|
|
1001
|
+
* @remarks Time O(N), Space O(1)
|
|
1002
|
+
* @param index - Zero-based index.
|
|
1003
|
+
* @returns Removed element or undefined.
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
* @example
|
|
1013
|
+
* // Remove by index
|
|
1014
|
+
* const list = new SinglyLinkedList<string>(['a', 'b', 'c']);
|
|
1015
|
+
* list.deleteAt(1);
|
|
1016
|
+
* console.log(list.toArray()); // ['a', 'c'];
|
|
1017
|
+
*/
|
|
926
1018
|
deleteAt(index) {
|
|
927
1019
|
if (index < 0 || index >= this._length) return void 0;
|
|
928
1020
|
if (index === 0) return this.shift();
|
|
@@ -935,11 +1027,24 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
935
1027
|
return value;
|
|
936
1028
|
}
|
|
937
1029
|
/**
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1030
|
+
* Delete the first match by value/node.
|
|
1031
|
+
* @remarks Time O(N), Space O(1)
|
|
1032
|
+
* @param [elementOrNode] - Element or node to remove; if omitted/undefined, nothing happens.
|
|
1033
|
+
* @returns True if removed.
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
* @example
|
|
1043
|
+
* // Remove first occurrence
|
|
1044
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3, 2]);
|
|
1045
|
+
* list.delete(2);
|
|
1046
|
+
* console.log(list.toArray()); // [1, 3, 2];
|
|
1047
|
+
*/
|
|
943
1048
|
delete(elementOrNode) {
|
|
944
1049
|
if (elementOrNode === void 0 || !this.head) return false;
|
|
945
1050
|
const node = this.isNode(elementOrNode) ? elementOrNode : this.getNode(elementOrNode);
|
|
@@ -956,12 +1061,25 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
956
1061
|
return true;
|
|
957
1062
|
}
|
|
958
1063
|
/**
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1064
|
+
* Insert a new element/node at an index, shifting following nodes.
|
|
1065
|
+
* @remarks Time O(N), Space O(1)
|
|
1066
|
+
* @param index - Zero-based index.
|
|
1067
|
+
* @param newElementOrNode - Element or node to insert.
|
|
1068
|
+
* @returns True if inserted.
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
* @example
|
|
1078
|
+
* // Insert at index
|
|
1079
|
+
* const list = new SinglyLinkedList<number>([1, 3]);
|
|
1080
|
+
* list.addAt(1, 2);
|
|
1081
|
+
* console.log(list.toArray()); // [1, 2, 3];
|
|
1082
|
+
*/
|
|
965
1083
|
addAt(index, newElementOrNode) {
|
|
966
1084
|
if (index < 0 || index > this._length) return false;
|
|
967
1085
|
if (index === 0) return this.unshift(newElementOrNode);
|
|
@@ -987,28 +1105,70 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
987
1105
|
return true;
|
|
988
1106
|
}
|
|
989
1107
|
/**
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1108
|
+
* Check whether the list is empty.
|
|
1109
|
+
* @remarks Time O(1), Space O(1)
|
|
1110
|
+
* @returns True if length is 0.
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
* @example
|
|
1121
|
+
* // Check empty
|
|
1122
|
+
* console.log(new SinglyLinkedList().isEmpty()); // true;
|
|
1123
|
+
*/
|
|
994
1124
|
isEmpty() {
|
|
995
1125
|
return this._length === 0;
|
|
996
1126
|
}
|
|
997
1127
|
/**
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1128
|
+
* Remove all nodes and reset length.
|
|
1129
|
+
* @remarks Time O(N), Space O(1)
|
|
1130
|
+
* @returns void
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
* @example
|
|
1141
|
+
* // Remove all
|
|
1142
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3]);
|
|
1143
|
+
* list.clear();
|
|
1144
|
+
* console.log(list.isEmpty()); // true;
|
|
1145
|
+
*/
|
|
1002
1146
|
clear() {
|
|
1003
1147
|
this._head = void 0;
|
|
1004
1148
|
this._tail = void 0;
|
|
1005
1149
|
this._length = 0;
|
|
1006
1150
|
}
|
|
1007
1151
|
/**
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1152
|
+
* Reverse the list in place.
|
|
1153
|
+
* @remarks Time O(N), Space O(1)
|
|
1154
|
+
* @returns This list.
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
* @example
|
|
1167
|
+
* // Reverse the list in-place
|
|
1168
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3, 4]);
|
|
1169
|
+
* list.reverse();
|
|
1170
|
+
* console.log([...list]); // [4, 3, 2, 1];
|
|
1171
|
+
*/
|
|
1012
1172
|
reverse() {
|
|
1013
1173
|
if (!this.head || this.head === this.tail) return this;
|
|
1014
1174
|
let prev;
|
|
@@ -1183,22 +1343,64 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
1183
1343
|
return false;
|
|
1184
1344
|
}
|
|
1185
1345
|
/**
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1346
|
+
* Deep clone this list (values are copied by reference).
|
|
1347
|
+
* @remarks Time O(N), Space O(N)
|
|
1348
|
+
* @returns A new list with the same element sequence.
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
* @example
|
|
1359
|
+
* // Deep copy
|
|
1360
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3]);
|
|
1361
|
+
* const copy = list.clone();
|
|
1362
|
+
* copy.pop();
|
|
1363
|
+
* console.log(list.length); // 3;
|
|
1364
|
+
* console.log(copy.length); // 2;
|
|
1365
|
+
*/
|
|
1190
1366
|
clone() {
|
|
1191
1367
|
const out = this._createInstance();
|
|
1192
1368
|
for (const v of this) out.push(v);
|
|
1193
1369
|
return out;
|
|
1194
1370
|
}
|
|
1195
1371
|
/**
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1372
|
+
* Filter values into a new list of the same class.
|
|
1373
|
+
* @remarks Time O(N), Space O(N)
|
|
1374
|
+
* @param callback - Predicate (value, index, list) → boolean to keep value.
|
|
1375
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
1376
|
+
* @returns A new list with kept values.
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
|
|
1380
|
+
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
* @example
|
|
1389
|
+
* // SinglyLinkedList filter and map operations
|
|
1390
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3, 4, 5]);
|
|
1391
|
+
*
|
|
1392
|
+
* // Filter even numbers
|
|
1393
|
+
* const filtered = list.filter(value => value % 2 === 0);
|
|
1394
|
+
* console.log(filtered.length); // 2;
|
|
1395
|
+
*
|
|
1396
|
+
* // Map to double values
|
|
1397
|
+
* const doubled = list.map(value => value * 2);
|
|
1398
|
+
* console.log(doubled.length); // 5;
|
|
1399
|
+
*
|
|
1400
|
+
* // Use reduce to sum
|
|
1401
|
+
* const sum = list.reduce((acc, value) => acc + value, 0);
|
|
1402
|
+
* console.log(sum); // 15;
|
|
1403
|
+
*/
|
|
1202
1404
|
filter(callback, thisArg) {
|
|
1203
1405
|
const out = this._createInstance();
|
|
1204
1406
|
let index = 0;
|
|
@@ -1222,15 +1424,31 @@ var SinglyLinkedList = class extends LinearLinkedBase {
|
|
|
1222
1424
|
return out;
|
|
1223
1425
|
}
|
|
1224
1426
|
/**
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1427
|
+
* Map values into a new list (possibly different element type).
|
|
1428
|
+
* @remarks Time O(N), Space O(N)
|
|
1429
|
+
* @template EM
|
|
1430
|
+
* @template RM
|
|
1431
|
+
* @param callback - Mapping function (value, index, list) → newElement.
|
|
1432
|
+
* @param [options] - Options for the output list (e.g., maxLen, toElementFn).
|
|
1433
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
1434
|
+
* @returns A new SinglyLinkedList with mapped values.
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
* @example
|
|
1447
|
+
* // Transform elements
|
|
1448
|
+
* const list = new SinglyLinkedList<number>([1, 2, 3]);
|
|
1449
|
+
* const doubled = list.map(n => n * 2);
|
|
1450
|
+
* console.log([...doubled]); // [2, 4, 6];
|
|
1451
|
+
*/
|
|
1234
1452
|
map(callback, options, thisArg) {
|
|
1235
1453
|
const out = this._createLike([], { ...options ?? {}, maxLen: this._maxLen });
|
|
1236
1454
|
let index = 0;
|
|
@@ -1506,11 +1724,37 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1506
1724
|
return elementNodeOrPredicate instanceof DoublyLinkedListNode;
|
|
1507
1725
|
}
|
|
1508
1726
|
/**
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1727
|
+
* Append an element/node to the tail.
|
|
1728
|
+
* @remarks Time O(1), Space O(1)
|
|
1729
|
+
* @param elementOrNode - Element or node to append.
|
|
1730
|
+
* @returns True when appended.
|
|
1731
|
+
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
* @example
|
|
1743
|
+
* // basic DoublyLinkedList creation and push operation
|
|
1744
|
+
* // Create a simple DoublyLinkedList with initial values
|
|
1745
|
+
* const list = new DoublyLinkedList([1, 2, 3, 4, 5]);
|
|
1746
|
+
*
|
|
1747
|
+
* // Verify the list maintains insertion order
|
|
1748
|
+
* console.log([...list]); // [1, 2, 3, 4, 5];
|
|
1749
|
+
*
|
|
1750
|
+
* // Check length
|
|
1751
|
+
* console.log(list.length); // 5;
|
|
1752
|
+
*
|
|
1753
|
+
* // Push a new element to the end
|
|
1754
|
+
* list.push(6);
|
|
1755
|
+
* console.log(list.length); // 6;
|
|
1756
|
+
* console.log([...list]); // [1, 2, 3, 4, 5, 6];
|
|
1757
|
+
*/
|
|
1514
1758
|
push(elementOrNode) {
|
|
1515
1759
|
const newNode = this._ensureNode(elementOrNode);
|
|
1516
1760
|
if (!this.head) {
|
|
@@ -1526,10 +1770,36 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1526
1770
|
return true;
|
|
1527
1771
|
}
|
|
1528
1772
|
/**
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1773
|
+
* Remove and return the tail element.
|
|
1774
|
+
* @remarks Time O(1), Space O(1)
|
|
1775
|
+
* @returns Removed element or undefined.
|
|
1776
|
+
|
|
1777
|
+
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
|
|
1787
|
+
* @example
|
|
1788
|
+
* // DoublyLinkedList pop and shift operations
|
|
1789
|
+
* const list = new DoublyLinkedList<number>([10, 20, 30, 40, 50]);
|
|
1790
|
+
*
|
|
1791
|
+
* // Pop removes from the end
|
|
1792
|
+
* const last = list.pop();
|
|
1793
|
+
* console.log(last); // 50;
|
|
1794
|
+
*
|
|
1795
|
+
* // Shift removes from the beginning
|
|
1796
|
+
* const first = list.shift();
|
|
1797
|
+
* console.log(first); // 10;
|
|
1798
|
+
*
|
|
1799
|
+
* // Verify remaining elements
|
|
1800
|
+
* console.log([...list]); // [20, 30, 40];
|
|
1801
|
+
* console.log(list.length); // 3;
|
|
1802
|
+
*/
|
|
1533
1803
|
pop() {
|
|
1534
1804
|
if (!this.tail) return void 0;
|
|
1535
1805
|
const removed = this.tail;
|
|
@@ -1544,10 +1814,26 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1544
1814
|
return removed.value;
|
|
1545
1815
|
}
|
|
1546
1816
|
/**
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1817
|
+
* Remove and return the head element.
|
|
1818
|
+
* @remarks Time O(1), Space O(1)
|
|
1819
|
+
* @returns Removed element or undefined.
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
|
|
1823
|
+
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
|
|
1827
|
+
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
* @example
|
|
1832
|
+
* // Remove from the front
|
|
1833
|
+
* const list = new DoublyLinkedList<number>([10, 20, 30]);
|
|
1834
|
+
* console.log(list.shift()); // 10;
|
|
1835
|
+
* console.log(list.first); // 20;
|
|
1836
|
+
*/
|
|
1551
1837
|
shift() {
|
|
1552
1838
|
if (!this.head) return void 0;
|
|
1553
1839
|
const removed = this.head;
|
|
@@ -1562,11 +1848,27 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1562
1848
|
return removed.value;
|
|
1563
1849
|
}
|
|
1564
1850
|
/**
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1851
|
+
* Prepend an element/node to the head.
|
|
1852
|
+
* @remarks Time O(1), Space O(1)
|
|
1853
|
+
* @param elementOrNode - Element or node to prepend.
|
|
1854
|
+
* @returns True when prepended.
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
* @example
|
|
1867
|
+
* // Add to the front
|
|
1868
|
+
* const list = new DoublyLinkedList<number>([2, 3]);
|
|
1869
|
+
* list.unshift(1);
|
|
1870
|
+
* console.log([...list]); // [1, 2, 3];
|
|
1871
|
+
*/
|
|
1570
1872
|
unshift(elementOrNode) {
|
|
1571
1873
|
const newNode = this._ensureNode(elementOrNode);
|
|
1572
1874
|
if (!this.head) {
|
|
@@ -1610,11 +1912,27 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1610
1912
|
return ans;
|
|
1611
1913
|
}
|
|
1612
1914
|
/**
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1915
|
+
* Get the element at a given index.
|
|
1916
|
+
* @remarks Time O(N), Space O(1)
|
|
1917
|
+
* @param index - Zero-based index.
|
|
1918
|
+
* @returns Element or undefined.
|
|
1919
|
+
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
* @example
|
|
1931
|
+
* // Access by index
|
|
1932
|
+
* const list = new DoublyLinkedList<string>(['a', 'b', 'c']);
|
|
1933
|
+
* console.log(list.at(1)); // 'b';
|
|
1934
|
+
* console.log(list.at(2)); // 'c';
|
|
1935
|
+
*/
|
|
1618
1936
|
at(index) {
|
|
1619
1937
|
if (index < 0 || index >= this._length) return void 0;
|
|
1620
1938
|
let current = this.head;
|
|
@@ -1622,11 +1940,23 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1622
1940
|
return current?.value;
|
|
1623
1941
|
}
|
|
1624
1942
|
/**
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1943
|
+
* Get the node reference at a given index.
|
|
1944
|
+
* @remarks Time O(N), Space O(1)
|
|
1945
|
+
* @param index - Zero-based index.
|
|
1946
|
+
* @returns Node or undefined.
|
|
1947
|
+
|
|
1948
|
+
|
|
1949
|
+
|
|
1950
|
+
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
* @example
|
|
1956
|
+
* // Get node at index
|
|
1957
|
+
* const list = new DoublyLinkedList<string>(['a', 'b', 'c']);
|
|
1958
|
+
* console.log(list.getNodeAt(1)?.value); // 'b';
|
|
1959
|
+
*/
|
|
1630
1960
|
getNodeAt(index) {
|
|
1631
1961
|
if (index < 0 || index >= this._length) return void 0;
|
|
1632
1962
|
let current = this.head;
|
|
@@ -1665,12 +1995,25 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1665
1995
|
return void 0;
|
|
1666
1996
|
}
|
|
1667
1997
|
/**
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1998
|
+
* Insert a new element/node at an index, shifting following nodes.
|
|
1999
|
+
* @remarks Time O(N), Space O(1)
|
|
2000
|
+
* @param index - Zero-based index.
|
|
2001
|
+
* @param newElementOrNode - Element or node to insert.
|
|
2002
|
+
* @returns True if inserted.
|
|
2003
|
+
|
|
2004
|
+
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
|
|
2011
|
+
* @example
|
|
2012
|
+
* // Insert at position
|
|
2013
|
+
* const list = new DoublyLinkedList<number>([1, 3]);
|
|
2014
|
+
* list.addAt(1, 2);
|
|
2015
|
+
* console.log(list.toArray()); // [1, 2, 3];
|
|
2016
|
+
*/
|
|
1674
2017
|
addAt(index, newElementOrNode) {
|
|
1675
2018
|
if (index < 0 || index > this._length) return false;
|
|
1676
2019
|
if (index === 0) return this.unshift(newElementOrNode);
|
|
@@ -1737,11 +2080,24 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1737
2080
|
return true;
|
|
1738
2081
|
}
|
|
1739
2082
|
/**
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
2083
|
+
* Delete the element at an index.
|
|
2084
|
+
* @remarks Time O(N), Space O(1)
|
|
2085
|
+
* @param index - Zero-based index.
|
|
2086
|
+
* @returns Removed element or undefined.
|
|
2087
|
+
|
|
2088
|
+
|
|
2089
|
+
|
|
2090
|
+
|
|
2091
|
+
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
|
|
2095
|
+
* @example
|
|
2096
|
+
* // Remove by index
|
|
2097
|
+
* const list = new DoublyLinkedList<string>(['a', 'b', 'c']);
|
|
2098
|
+
* list.deleteAt(1);
|
|
2099
|
+
* console.log(list.toArray()); // ['a', 'c'];
|
|
2100
|
+
*/
|
|
1745
2101
|
deleteAt(index) {
|
|
1746
2102
|
if (index < 0 || index >= this._length) return;
|
|
1747
2103
|
if (index === 0) return this.shift();
|
|
@@ -1755,11 +2111,24 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1755
2111
|
return removedNode.value;
|
|
1756
2112
|
}
|
|
1757
2113
|
/**
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
2114
|
+
* Delete the first match by value/node.
|
|
2115
|
+
* @remarks Time O(N), Space O(1)
|
|
2116
|
+
* @param [elementOrNode] - Element or node to remove.
|
|
2117
|
+
* @returns True if removed.
|
|
2118
|
+
|
|
2119
|
+
|
|
2120
|
+
|
|
2121
|
+
|
|
2122
|
+
|
|
2123
|
+
|
|
2124
|
+
|
|
2125
|
+
|
|
2126
|
+
* @example
|
|
2127
|
+
* // Remove first occurrence
|
|
2128
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3, 2]);
|
|
2129
|
+
* list.delete(2);
|
|
2130
|
+
* console.log(list.toArray()); // [1, 3, 2];
|
|
2131
|
+
*/
|
|
1763
2132
|
delete(elementOrNode) {
|
|
1764
2133
|
const node = this.getNode(elementOrNode);
|
|
1765
2134
|
if (!node) return false;
|
|
@@ -1775,29 +2144,68 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1775
2144
|
return true;
|
|
1776
2145
|
}
|
|
1777
2146
|
/**
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2147
|
+
* Check whether the list is empty.
|
|
2148
|
+
* @remarks Time O(1), Space O(1)
|
|
2149
|
+
* @returns True if length is 0.
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
* @example
|
|
2160
|
+
* // Check empty
|
|
2161
|
+
* console.log(new DoublyLinkedList().isEmpty()); // true;
|
|
2162
|
+
*/
|
|
1782
2163
|
isEmpty() {
|
|
1783
2164
|
return this._length === 0;
|
|
1784
2165
|
}
|
|
1785
2166
|
/**
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
2167
|
+
* Remove all nodes and reset length.
|
|
2168
|
+
* @remarks Time O(N), Space O(1)
|
|
2169
|
+
* @returns void
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
|
|
2174
|
+
|
|
2175
|
+
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
* @example
|
|
2180
|
+
* // Remove all
|
|
2181
|
+
* const list = new DoublyLinkedList<number>([1, 2]);
|
|
2182
|
+
* list.clear();
|
|
2183
|
+
* console.log(list.isEmpty()); // true;
|
|
2184
|
+
*/
|
|
1790
2185
|
clear() {
|
|
1791
2186
|
this._head = void 0;
|
|
1792
2187
|
this._tail = void 0;
|
|
1793
2188
|
this._length = 0;
|
|
1794
2189
|
}
|
|
1795
2190
|
/**
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
2191
|
+
* Find the first value matching a predicate scanning forward.
|
|
2192
|
+
* @remarks Time O(N), Space O(1)
|
|
2193
|
+
* @param elementNodeOrPredicate - Element, node, or predicate to match.
|
|
2194
|
+
* @returns Matched value or undefined.
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
|
|
2200
|
+
|
|
2201
|
+
|
|
2202
|
+
|
|
2203
|
+
* @example
|
|
2204
|
+
* // Search with predicate
|
|
2205
|
+
* const list = new DoublyLinkedList<number>([10, 20, 30]);
|
|
2206
|
+
* const found = list.search(node => node.value > 15);
|
|
2207
|
+
* console.log(found); // 20;
|
|
2208
|
+
*/
|
|
1801
2209
|
search(elementNodeOrPredicate) {
|
|
1802
2210
|
const predicate = this._ensurePredicate(elementNodeOrPredicate);
|
|
1803
2211
|
let current = this.head;
|
|
@@ -1808,11 +2216,25 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1808
2216
|
return void 0;
|
|
1809
2217
|
}
|
|
1810
2218
|
/**
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
2219
|
+
* Find the first value matching a predicate scanning backward.
|
|
2220
|
+
* @remarks Time O(N), Space O(1)
|
|
2221
|
+
* @param elementNodeOrPredicate - Element, node, or predicate to match.
|
|
2222
|
+
* @returns Matched value or undefined.
|
|
2223
|
+
|
|
2224
|
+
|
|
2225
|
+
|
|
2226
|
+
|
|
2227
|
+
|
|
2228
|
+
|
|
2229
|
+
|
|
2230
|
+
|
|
2231
|
+
* @example
|
|
2232
|
+
* // Find value scanning from tail
|
|
2233
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3, 4]);
|
|
2234
|
+
* // getBackward scans from tail to head, returns first match
|
|
2235
|
+
* const found = list.getBackward(node => node.value < 4);
|
|
2236
|
+
* console.log(found); // 3;
|
|
2237
|
+
*/
|
|
1816
2238
|
getBackward(elementNodeOrPredicate) {
|
|
1817
2239
|
const predicate = this._ensurePredicate(elementNodeOrPredicate);
|
|
1818
2240
|
let current = this.tail;
|
|
@@ -1823,10 +2245,26 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1823
2245
|
return void 0;
|
|
1824
2246
|
}
|
|
1825
2247
|
/**
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2248
|
+
* Reverse the list in place.
|
|
2249
|
+
* @remarks Time O(N), Space O(1)
|
|
2250
|
+
* @returns This list.
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
|
|
2258
|
+
|
|
2259
|
+
|
|
2260
|
+
|
|
2261
|
+
|
|
2262
|
+
* @example
|
|
2263
|
+
* // Reverse in-place
|
|
2264
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3]);
|
|
2265
|
+
* list.reverse();
|
|
2266
|
+
* console.log([...list]); // [3, 2, 1];
|
|
2267
|
+
*/
|
|
1830
2268
|
reverse() {
|
|
1831
2269
|
let current = this.head;
|
|
1832
2270
|
[this._head, this._tail] = [this.tail, this.head];
|
|
@@ -1848,22 +2286,53 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1848
2286
|
return this;
|
|
1849
2287
|
}
|
|
1850
2288
|
/**
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
2289
|
+
* Deep clone this list (values are copied by reference).
|
|
2290
|
+
* @remarks Time O(N), Space O(N)
|
|
2291
|
+
* @returns A new list with the same element sequence.
|
|
2292
|
+
|
|
2293
|
+
|
|
2294
|
+
|
|
2295
|
+
|
|
2296
|
+
|
|
2297
|
+
|
|
2298
|
+
|
|
2299
|
+
|
|
2300
|
+
|
|
2301
|
+
* @example
|
|
2302
|
+
* // Deep copy
|
|
2303
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3]);
|
|
2304
|
+
* const copy = list.clone();
|
|
2305
|
+
* copy.pop();
|
|
2306
|
+
* console.log(list.length); // 3;
|
|
2307
|
+
*/
|
|
1855
2308
|
clone() {
|
|
1856
2309
|
const out = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen });
|
|
1857
2310
|
for (const v of this) out.push(v);
|
|
1858
2311
|
return out;
|
|
1859
2312
|
}
|
|
1860
2313
|
/**
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2314
|
+
* Filter values into a new list of the same class.
|
|
2315
|
+
* @remarks Time O(N), Space O(N)
|
|
2316
|
+
* @param callback - Predicate (value, index, list) → boolean to keep value.
|
|
2317
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
2318
|
+
* @returns A new list with kept values.
|
|
2319
|
+
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
|
|
2323
|
+
|
|
2324
|
+
|
|
2325
|
+
|
|
2326
|
+
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
* @example
|
|
2331
|
+
* // Filter elements
|
|
2332
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3, 4, 5]);
|
|
2333
|
+
* const evens = list.filter(n => n % 2 === 0);
|
|
2334
|
+
* console.log([...evens]); // [2, 4];
|
|
2335
|
+
*/
|
|
1867
2336
|
filter(callback, thisArg) {
|
|
1868
2337
|
const out = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen });
|
|
1869
2338
|
let index = 0;
|
|
@@ -1887,15 +2356,40 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1887
2356
|
return out;
|
|
1888
2357
|
}
|
|
1889
2358
|
/**
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2359
|
+
* Map values into a new list (possibly different element type).
|
|
2360
|
+
* @remarks Time O(N), Space O(N)
|
|
2361
|
+
* @template EM
|
|
2362
|
+
* @template RM
|
|
2363
|
+
* @param callback - Mapping function (value, index, list) → newElement.
|
|
2364
|
+
* @param [options] - Options for the output list (e.g., maxLen, toElementFn).
|
|
2365
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
2366
|
+
* @returns A new DoublyLinkedList with mapped values.
|
|
2367
|
+
|
|
2368
|
+
|
|
2369
|
+
|
|
2370
|
+
|
|
2371
|
+
|
|
2372
|
+
|
|
2373
|
+
|
|
2374
|
+
|
|
2375
|
+
|
|
2376
|
+
|
|
2377
|
+
|
|
2378
|
+
* @example
|
|
2379
|
+
* // DoublyLinkedList for...of iteration and map operation
|
|
2380
|
+
* const list = new DoublyLinkedList<number>([1, 2, 3, 4, 5]);
|
|
2381
|
+
*
|
|
2382
|
+
* // Iterate through list
|
|
2383
|
+
* const doubled = list.map(value => value * 2);
|
|
2384
|
+
* console.log(doubled.length); // 5;
|
|
2385
|
+
*
|
|
2386
|
+
* // Use for...of loop
|
|
2387
|
+
* const result: number[] = [];
|
|
2388
|
+
* for (const item of list) {
|
|
2389
|
+
* result.push(item);
|
|
2390
|
+
* }
|
|
2391
|
+
* console.log(result); // [1, 2, 3, 4, 5];
|
|
2392
|
+
*/
|
|
1899
2393
|
map(callback, options, thisArg) {
|
|
1900
2394
|
const out = this._createLike([], { ...options ?? {}, maxLen: this._maxLen });
|
|
1901
2395
|
let index = 0;
|
|
@@ -1984,6 +2478,237 @@ var DoublyLinkedList = class extends LinearLinkedBase {
|
|
|
1984
2478
|
}
|
|
1985
2479
|
};
|
|
1986
2480
|
|
|
2481
|
+
// src/common/error.ts
|
|
2482
|
+
var ERR = {
|
|
2483
|
+
// Range / index
|
|
2484
|
+
indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"),
|
|
2485
|
+
invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"),
|
|
2486
|
+
// Type / argument
|
|
2487
|
+
invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"),
|
|
2488
|
+
comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"),
|
|
2489
|
+
invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"),
|
|
2490
|
+
notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"),
|
|
2491
|
+
invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"),
|
|
2492
|
+
invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"),
|
|
2493
|
+
invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"),
|
|
2494
|
+
reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"),
|
|
2495
|
+
callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"),
|
|
2496
|
+
// State / operation
|
|
2497
|
+
invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"),
|
|
2498
|
+
// Matrix
|
|
2499
|
+
matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"),
|
|
2500
|
+
matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"),
|
|
2501
|
+
matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"),
|
|
2502
|
+
matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"),
|
|
2503
|
+
matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch")
|
|
2504
|
+
};
|
|
2505
|
+
|
|
2506
|
+
// src/common/index.ts
|
|
2507
|
+
var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
|
|
2508
|
+
DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
|
|
2509
|
+
DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
|
|
2510
|
+
return DFSOperation2;
|
|
2511
|
+
})(DFSOperation || {});
|
|
2512
|
+
var Range = class {
|
|
2513
|
+
constructor(low, high, includeLow = true, includeHigh = true) {
|
|
2514
|
+
this.low = low;
|
|
2515
|
+
this.high = high;
|
|
2516
|
+
this.includeLow = includeLow;
|
|
2517
|
+
this.includeHigh = includeHigh;
|
|
2518
|
+
}
|
|
2519
|
+
static {
|
|
2520
|
+
__name(this, "Range");
|
|
2521
|
+
}
|
|
2522
|
+
// Determine whether a key is within the range
|
|
2523
|
+
isInRange(key, comparator) {
|
|
2524
|
+
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
|
|
2525
|
+
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
|
|
2526
|
+
return lowCheck && highCheck;
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
|
|
2530
|
+
// src/data-structures/base/iterable-entry-base.ts
|
|
2531
|
+
var IterableEntryBase = class {
|
|
2532
|
+
static {
|
|
2533
|
+
__name(this, "IterableEntryBase");
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Default iterator yielding `[key, value]` entries.
|
|
2537
|
+
* @returns Iterator of `[K, V]`.
|
|
2538
|
+
* @remarks Time O(n) to iterate, Space O(1)
|
|
2539
|
+
*/
|
|
2540
|
+
*[Symbol.iterator](...args) {
|
|
2541
|
+
yield* this._getIterator(...args);
|
|
2542
|
+
}
|
|
2543
|
+
/**
|
|
2544
|
+
* Iterate over `[key, value]` pairs (may yield `undefined` values).
|
|
2545
|
+
* @returns Iterator of `[K, V | undefined]`.
|
|
2546
|
+
* @remarks Time O(n), Space O(1)
|
|
2547
|
+
*/
|
|
2548
|
+
*entries() {
|
|
2549
|
+
for (const item of this) {
|
|
2550
|
+
yield item;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Iterate over keys only.
|
|
2555
|
+
* @returns Iterator of keys.
|
|
2556
|
+
* @remarks Time O(n), Space O(1)
|
|
2557
|
+
*/
|
|
2558
|
+
*keys() {
|
|
2559
|
+
for (const item of this) {
|
|
2560
|
+
yield item[0];
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Iterate over values only.
|
|
2565
|
+
* @returns Iterator of values.
|
|
2566
|
+
* @remarks Time O(n), Space O(1)
|
|
2567
|
+
*/
|
|
2568
|
+
*values() {
|
|
2569
|
+
for (const item of this) {
|
|
2570
|
+
yield item[1];
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Test whether all entries satisfy the predicate.
|
|
2575
|
+
* @param predicate - `(key, value, index, self) => boolean`.
|
|
2576
|
+
* @param thisArg - Optional `this` for callback.
|
|
2577
|
+
* @returns `true` if all pass; otherwise `false`.
|
|
2578
|
+
* @remarks Time O(n), Space O(1)
|
|
2579
|
+
*/
|
|
2580
|
+
every(predicate, thisArg) {
|
|
2581
|
+
let index = 0;
|
|
2582
|
+
for (const item of this) {
|
|
2583
|
+
if (!predicate.call(thisArg, item[1], item[0], index++, this)) {
|
|
2584
|
+
return false;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
return true;
|
|
2588
|
+
}
|
|
2589
|
+
/**
|
|
2590
|
+
* Test whether any entry satisfies the predicate.
|
|
2591
|
+
* @param predicate - `(key, value, index, self) => boolean`.
|
|
2592
|
+
* @param thisArg - Optional `this` for callback.
|
|
2593
|
+
* @returns `true` if any passes; otherwise `false`.
|
|
2594
|
+
* @remarks Time O(n), Space O(1)
|
|
2595
|
+
*/
|
|
2596
|
+
some(predicate, thisArg) {
|
|
2597
|
+
let index = 0;
|
|
2598
|
+
for (const item of this) {
|
|
2599
|
+
if (predicate.call(thisArg, item[1], item[0], index++, this)) {
|
|
2600
|
+
return true;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
return false;
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Visit each entry, left-to-right.
|
|
2607
|
+
* @param callbackfn - `(key, value, index, self) => void`.
|
|
2608
|
+
* @param thisArg - Optional `this` for callback.
|
|
2609
|
+
* @remarks Time O(n), Space O(1)
|
|
2610
|
+
*/
|
|
2611
|
+
forEach(callbackfn, thisArg) {
|
|
2612
|
+
let index = 0;
|
|
2613
|
+
for (const item of this) {
|
|
2614
|
+
const [key, value] = item;
|
|
2615
|
+
callbackfn.call(thisArg, value, key, index++, this);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* Find the first entry that matches a predicate.
|
|
2620
|
+
* @param callbackfn - `(key, value, index, self) => boolean`.
|
|
2621
|
+
* @param thisArg - Optional `this` for callback.
|
|
2622
|
+
* @returns Matching `[key, value]` or `undefined`.
|
|
2623
|
+
* @remarks Time O(n), Space O(1)
|
|
2624
|
+
*/
|
|
2625
|
+
find(callbackfn, thisArg) {
|
|
2626
|
+
let index = 0;
|
|
2627
|
+
for (const item of this) {
|
|
2628
|
+
const [key, value] = item;
|
|
2629
|
+
if (callbackfn.call(thisArg, value, key, index++, this)) return item;
|
|
2630
|
+
}
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Whether the given key exists.
|
|
2635
|
+
* @param key - Key to test.
|
|
2636
|
+
* @returns `true` if found; otherwise `false`.
|
|
2637
|
+
* @remarks Time O(n) generic, Space O(1)
|
|
2638
|
+
*/
|
|
2639
|
+
has(key) {
|
|
2640
|
+
for (const item of this) {
|
|
2641
|
+
const [itemKey] = item;
|
|
2642
|
+
if (itemKey === key) return true;
|
|
2643
|
+
}
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Whether there exists an entry with the given value.
|
|
2648
|
+
* @param value - Value to test.
|
|
2649
|
+
* @returns `true` if found; otherwise `false`.
|
|
2650
|
+
* @remarks Time O(n), Space O(1)
|
|
2651
|
+
*/
|
|
2652
|
+
hasValue(value) {
|
|
2653
|
+
for (const [, elementValue] of this) {
|
|
2654
|
+
if (elementValue === value) return true;
|
|
2655
|
+
}
|
|
2656
|
+
return false;
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* Get the value under a key.
|
|
2660
|
+
* @param key - Key to look up.
|
|
2661
|
+
* @returns Value or `undefined`.
|
|
2662
|
+
* @remarks Time O(n) generic, Space O(1)
|
|
2663
|
+
*/
|
|
2664
|
+
get(key) {
|
|
2665
|
+
for (const item of this) {
|
|
2666
|
+
const [itemKey, value] = item;
|
|
2667
|
+
if (itemKey === key) return value;
|
|
2668
|
+
}
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
/**
|
|
2672
|
+
* Reduce entries into a single accumulator.
|
|
2673
|
+
* @param callbackfn - `(acc, value, key, index, self) => acc`.
|
|
2674
|
+
* @param initialValue - Initial accumulator.
|
|
2675
|
+
* @returns Final accumulator.
|
|
2676
|
+
* @remarks Time O(n), Space O(1)
|
|
2677
|
+
*/
|
|
2678
|
+
reduce(callbackfn, initialValue) {
|
|
2679
|
+
let accumulator = initialValue;
|
|
2680
|
+
let index = 0;
|
|
2681
|
+
for (const item of this) {
|
|
2682
|
+
const [key, value] = item;
|
|
2683
|
+
accumulator = callbackfn(accumulator, value, key, index++, this);
|
|
2684
|
+
}
|
|
2685
|
+
return accumulator;
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Converts data structure to `[key, value]` pairs.
|
|
2689
|
+
* @returns Array of entries.
|
|
2690
|
+
* @remarks Time O(n), Space O(n)
|
|
2691
|
+
*/
|
|
2692
|
+
toArray() {
|
|
2693
|
+
return [...this];
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Visualize the iterable as an array of `[key, value]` pairs (or a custom string).
|
|
2697
|
+
* @returns Array of entries (default) or a string.
|
|
2698
|
+
* @remarks Time O(n), Space O(n)
|
|
2699
|
+
*/
|
|
2700
|
+
toVisual() {
|
|
2701
|
+
return [...this];
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Print a human-friendly representation to the console.
|
|
2705
|
+
* @remarks Time O(n), Space O(n)
|
|
2706
|
+
*/
|
|
2707
|
+
print() {
|
|
2708
|
+
console.log(this.toVisual());
|
|
2709
|
+
}
|
|
2710
|
+
};
|
|
2711
|
+
|
|
1987
2712
|
// src/data-structures/linked-list/skip-linked-list.ts
|
|
1988
2713
|
var SkipListNode = class {
|
|
1989
2714
|
static {
|
|
@@ -1995,136 +2720,655 @@ var SkipListNode = class {
|
|
|
1995
2720
|
constructor(key, value, level) {
|
|
1996
2721
|
this.key = key;
|
|
1997
2722
|
this.value = value;
|
|
1998
|
-
this.forward = new Array(level);
|
|
2723
|
+
this.forward = new Array(level).fill(void 0);
|
|
1999
2724
|
}
|
|
2000
2725
|
};
|
|
2001
|
-
var SkipList = class {
|
|
2726
|
+
var SkipList = class _SkipList extends IterableEntryBase {
|
|
2002
2727
|
static {
|
|
2003
2728
|
__name(this, "SkipList");
|
|
2004
2729
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
if (
|
|
2012
|
-
|
|
2730
|
+
#comparator;
|
|
2731
|
+
#isDefaultComparator;
|
|
2732
|
+
constructor(entries = [], options = {}) {
|
|
2733
|
+
super();
|
|
2734
|
+
const { comparator, toEntryFn, maxLevel, probability } = options;
|
|
2735
|
+
if (typeof maxLevel === "number" && maxLevel > 0) this._maxLevel = maxLevel;
|
|
2736
|
+
if (typeof probability === "number" && probability > 0 && probability < 1) this._probability = probability;
|
|
2737
|
+
this.#isDefaultComparator = comparator === void 0;
|
|
2738
|
+
this.#comparator = comparator ?? _SkipList.createDefaultComparator();
|
|
2739
|
+
this._head = new SkipListNode(void 0, void 0, this._maxLevel);
|
|
2740
|
+
for (const item of entries) {
|
|
2741
|
+
let k;
|
|
2742
|
+
let v;
|
|
2743
|
+
if (toEntryFn) {
|
|
2744
|
+
[k, v] = toEntryFn(item);
|
|
2745
|
+
} else {
|
|
2746
|
+
if (!Array.isArray(item) || item.length < 2) {
|
|
2747
|
+
throw new TypeError(ERR.invalidEntry("SkipList"));
|
|
2748
|
+
}
|
|
2749
|
+
[k, v] = item;
|
|
2750
|
+
}
|
|
2751
|
+
this.set(k, v);
|
|
2013
2752
|
}
|
|
2014
2753
|
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2754
|
+
/**
|
|
2755
|
+
* Creates a default comparator supporting number, string, Date, and bigint.
|
|
2756
|
+
*/
|
|
2757
|
+
static createDefaultComparator() {
|
|
2758
|
+
return (a, b) => {
|
|
2759
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
2760
|
+
if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN("SkipList"));
|
|
2761
|
+
return a - b;
|
|
2762
|
+
}
|
|
2763
|
+
if (typeof a === "string" && typeof b === "string") {
|
|
2764
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2765
|
+
}
|
|
2766
|
+
if (a instanceof Date && b instanceof Date) {
|
|
2767
|
+
const ta = a.getTime(), tb = b.getTime();
|
|
2768
|
+
if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("SkipList"));
|
|
2769
|
+
return ta - tb;
|
|
2770
|
+
}
|
|
2771
|
+
if (typeof a === "bigint" && typeof b === "bigint") {
|
|
2772
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2773
|
+
}
|
|
2774
|
+
throw new TypeError(ERR.comparatorRequired("SkipList"));
|
|
2775
|
+
};
|
|
2018
2776
|
}
|
|
2777
|
+
// ─── Internal state ──────────────────────────────────────────
|
|
2778
|
+
_head;
|
|
2019
2779
|
_level = 0;
|
|
2020
|
-
|
|
2021
|
-
return this._level;
|
|
2022
|
-
}
|
|
2780
|
+
_size = 0;
|
|
2023
2781
|
_maxLevel = 16;
|
|
2782
|
+
_probability = 0.5;
|
|
2783
|
+
// ─── Size & lifecycle ────────────────────────────────────────
|
|
2784
|
+
get size() {
|
|
2785
|
+
return this._size;
|
|
2786
|
+
}
|
|
2024
2787
|
get maxLevel() {
|
|
2025
2788
|
return this._maxLevel;
|
|
2026
2789
|
}
|
|
2027
|
-
_probability = 0.5;
|
|
2028
2790
|
get probability() {
|
|
2029
2791
|
return this._probability;
|
|
2030
2792
|
}
|
|
2031
|
-
get
|
|
2032
|
-
|
|
2033
|
-
|
|
2793
|
+
get comparator() {
|
|
2794
|
+
return this.#comparator;
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Check if empty
|
|
2798
|
+
|
|
2799
|
+
|
|
2800
|
+
|
|
2801
|
+
|
|
2802
|
+
|
|
2803
|
+
|
|
2804
|
+
|
|
2805
|
+
|
|
2806
|
+
* @example
|
|
2807
|
+
* // Check if empty
|
|
2808
|
+
* const sl = new SkipList<number, string>();
|
|
2809
|
+
* console.log(sl.isEmpty()); // true;
|
|
2810
|
+
*/
|
|
2811
|
+
isEmpty() {
|
|
2812
|
+
return this._size === 0;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Remove all entries
|
|
2816
|
+
|
|
2817
|
+
|
|
2818
|
+
|
|
2819
|
+
|
|
2820
|
+
|
|
2821
|
+
|
|
2822
|
+
|
|
2823
|
+
|
|
2824
|
+
* @example
|
|
2825
|
+
* // Remove all entries
|
|
2826
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
|
|
2827
|
+
* sl.clear();
|
|
2828
|
+
* console.log(sl.isEmpty()); // true;
|
|
2829
|
+
*/
|
|
2830
|
+
clear() {
|
|
2831
|
+
this._head = new SkipListNode(void 0, void 0, this._maxLevel);
|
|
2832
|
+
this._level = 0;
|
|
2833
|
+
this._size = 0;
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* Create independent copy
|
|
2837
|
+
|
|
2838
|
+
|
|
2839
|
+
|
|
2840
|
+
|
|
2841
|
+
|
|
2842
|
+
|
|
2843
|
+
|
|
2844
|
+
|
|
2845
|
+
* @example
|
|
2846
|
+
* // Create independent copy
|
|
2847
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
|
|
2848
|
+
* const copy = sl.clone();
|
|
2849
|
+
* copy.delete(1);
|
|
2850
|
+
* console.log(sl.has(1)); // true;
|
|
2851
|
+
*/
|
|
2852
|
+
clone() {
|
|
2853
|
+
return new _SkipList(this, {
|
|
2854
|
+
comparator: this.#isDefaultComparator ? void 0 : this.#comparator,
|
|
2855
|
+
maxLevel: this._maxLevel,
|
|
2856
|
+
probability: this._probability
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
// ─── Core CRUD ───────────────────────────────────────────────
|
|
2860
|
+
/**
|
|
2861
|
+
* Insert or update a key-value pair. Returns `this` for chaining.
|
|
2862
|
+
* Unique keys only — if key exists, value is updated in place.
|
|
2863
|
+
|
|
2864
|
+
|
|
2865
|
+
|
|
2866
|
+
|
|
2867
|
+
|
|
2868
|
+
|
|
2869
|
+
|
|
2870
|
+
|
|
2871
|
+
|
|
2872
|
+
|
|
2873
|
+
|
|
2874
|
+
* @example
|
|
2875
|
+
* // In-memory sorted key-value store
|
|
2876
|
+
* const store = new SkipList<number, string>();
|
|
2877
|
+
*
|
|
2878
|
+
* store.set(3, 'three');
|
|
2879
|
+
* store.set(1, 'one');
|
|
2880
|
+
* store.set(5, 'five');
|
|
2881
|
+
* store.set(2, 'two');
|
|
2882
|
+
*
|
|
2883
|
+
* console.log(store.get(3)); // 'three';
|
|
2884
|
+
* console.log(store.get(1)); // 'one';
|
|
2885
|
+
* console.log(store.get(5)); // 'five';
|
|
2886
|
+
*
|
|
2887
|
+
* // Update existing key
|
|
2888
|
+
* store.set(3, 'THREE');
|
|
2889
|
+
* console.log(store.get(3)); // 'THREE';
|
|
2890
|
+
*/
|
|
2891
|
+
set(key, value) {
|
|
2892
|
+
const cmp = this.#comparator;
|
|
2893
|
+
const update = this._findUpdate(key);
|
|
2894
|
+
const existing = update[0].forward[0];
|
|
2895
|
+
if (existing && cmp(existing.key, key) === 0) {
|
|
2896
|
+
existing.value = value;
|
|
2897
|
+
return this;
|
|
2898
|
+
}
|
|
2899
|
+
const newLevel = this._randomLevel();
|
|
2900
|
+
const newNode = new SkipListNode(key, value, newLevel);
|
|
2901
|
+
if (newLevel > this._level) {
|
|
2902
|
+
for (let i = this._level; i < newLevel; i++) {
|
|
2903
|
+
update[i] = this._head;
|
|
2904
|
+
}
|
|
2905
|
+
this._level = newLevel;
|
|
2906
|
+
}
|
|
2907
|
+
for (let i = 0; i < newLevel; i++) {
|
|
2908
|
+
newNode.forward[i] = update[i].forward[i];
|
|
2909
|
+
update[i].forward[i] = newNode;
|
|
2910
|
+
}
|
|
2911
|
+
this._size++;
|
|
2912
|
+
return this;
|
|
2034
2913
|
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2914
|
+
/**
|
|
2915
|
+
* Get the value for a key, or `undefined` if not found.
|
|
2916
|
+
* Overrides base O(n) with O(log n) skip-list search.
|
|
2917
|
+
|
|
2918
|
+
|
|
2919
|
+
|
|
2920
|
+
|
|
2921
|
+
|
|
2922
|
+
|
|
2923
|
+
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
|
|
2927
|
+
|
|
2928
|
+
* @example
|
|
2929
|
+
* // Building a sorted index
|
|
2930
|
+
* type Product = { id: number; name: string; price: number };
|
|
2931
|
+
* const products: Product[] = [
|
|
2932
|
+
* { id: 1, name: 'Widget', price: 25 },
|
|
2933
|
+
* { id: 2, name: 'Gadget', price: 50 },
|
|
2934
|
+
* { id: 3, name: 'Doohickey', price: 15 }
|
|
2935
|
+
* ];
|
|
2936
|
+
*
|
|
2937
|
+
* const index = new SkipList<number, Product>(products as any, {
|
|
2938
|
+
* toEntryFn: (p: any) => [p.price, p]
|
|
2939
|
+
* });
|
|
2940
|
+
*
|
|
2941
|
+
* // Iterate in sorted order by price
|
|
2942
|
+
* const names = [...index.values()].map(p => p!.name);
|
|
2943
|
+
* console.log(names); // ['Doohickey', 'Widget', 'Gadget'];
|
|
2944
|
+
*
|
|
2945
|
+
* // Range search: products between $20 and $60
|
|
2946
|
+
* const range = index.rangeSearch([20, 60]);
|
|
2947
|
+
* console.log(range.map(([, p]) => p!.name)); // ['Widget', 'Gadget'];
|
|
2948
|
+
*/
|
|
2949
|
+
get(key) {
|
|
2950
|
+
const node = this._findNode(key);
|
|
2951
|
+
return node ? node.value : void 0;
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Check if a key exists.
|
|
2955
|
+
* Overrides base O(n) with O(log n) skip-list search.
|
|
2956
|
+
|
|
2957
|
+
|
|
2958
|
+
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
|
|
2962
|
+
|
|
2963
|
+
|
|
2964
|
+
|
|
2965
|
+
|
|
2966
|
+
|
|
2967
|
+
* @example
|
|
2968
|
+
* // Check key existence
|
|
2969
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [3, 'c'], [5, 'e']]);
|
|
2970
|
+
* console.log(sl.has(3)); // true;
|
|
2971
|
+
* console.log(sl.has(4)); // false;
|
|
2972
|
+
*/
|
|
2973
|
+
has(key) {
|
|
2974
|
+
return this._findNode(key) !== void 0;
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Delete a key. Returns `true` if the key was found and removed.
|
|
2978
|
+
|
|
2979
|
+
|
|
2980
|
+
|
|
2981
|
+
|
|
2982
|
+
|
|
2983
|
+
|
|
2984
|
+
|
|
2985
|
+
|
|
2986
|
+
|
|
2987
|
+
|
|
2988
|
+
|
|
2989
|
+
* @example
|
|
2990
|
+
* // Fast lookup with deletion
|
|
2991
|
+
* const cache = new SkipList<string, number>();
|
|
2992
|
+
*
|
|
2993
|
+
* cache.set('alpha', 1);
|
|
2994
|
+
* cache.set('beta', 2);
|
|
2995
|
+
* cache.set('gamma', 3);
|
|
2996
|
+
*
|
|
2997
|
+
* console.log(cache.has('beta')); // true;
|
|
2998
|
+
* cache.delete('beta');
|
|
2999
|
+
* console.log(cache.has('beta')); // false;
|
|
3000
|
+
* console.log(cache.size); // 2;
|
|
3001
|
+
*/
|
|
3002
|
+
delete(key) {
|
|
3003
|
+
const cmp = this.#comparator;
|
|
3004
|
+
const update = this._findUpdate(key);
|
|
3005
|
+
const target = update[0].forward[0];
|
|
3006
|
+
if (!target || cmp(target.key, key) !== 0) return false;
|
|
3007
|
+
for (let i = 0; i < this._level; i++) {
|
|
3008
|
+
if (update[i].forward[i] !== target) break;
|
|
3009
|
+
update[i].forward[i] = target.forward[i];
|
|
3010
|
+
}
|
|
3011
|
+
while (this._level > 0 && !this._head.forward[this._level - 1]) {
|
|
3012
|
+
this._level--;
|
|
3013
|
+
}
|
|
3014
|
+
this._size--;
|
|
3015
|
+
return true;
|
|
3016
|
+
}
|
|
3017
|
+
// ─── Navigation ──────────────────────────────────────────────
|
|
3018
|
+
/**
|
|
3019
|
+
* Returns the first (smallest key) entry, or `undefined` if empty.
|
|
3020
|
+
|
|
3021
|
+
|
|
3022
|
+
|
|
3023
|
+
|
|
3024
|
+
|
|
3025
|
+
|
|
3026
|
+
|
|
3027
|
+
|
|
3028
|
+
|
|
3029
|
+
|
|
3030
|
+
|
|
3031
|
+
* @example
|
|
3032
|
+
* // Access the minimum entry
|
|
3033
|
+
* const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
|
|
3034
|
+
* console.log(sl.first()); // [1, 'a'];
|
|
3035
|
+
*/
|
|
3036
|
+
first() {
|
|
3037
|
+
const node = this._head.forward[0];
|
|
3038
|
+
return node ? [node.key, node.value] : void 0;
|
|
3039
|
+
}
|
|
3040
|
+
/**
|
|
3041
|
+
* Returns the last (largest key) entry, or `undefined` if empty.
|
|
3042
|
+
|
|
3043
|
+
|
|
3044
|
+
|
|
3045
|
+
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
|
|
3049
|
+
|
|
3050
|
+
|
|
3051
|
+
|
|
3052
|
+
|
|
3053
|
+
* @example
|
|
3054
|
+
* // Access the maximum entry
|
|
3055
|
+
* const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
|
|
3056
|
+
* console.log(sl.last()); // [5, 'e'];
|
|
3057
|
+
*/
|
|
3058
|
+
last() {
|
|
3059
|
+
let current = this._head;
|
|
3060
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
2038
3061
|
while (current.forward[i]) {
|
|
2039
3062
|
current = current.forward[i];
|
|
2040
3063
|
}
|
|
2041
3064
|
}
|
|
2042
|
-
return current.value;
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
3065
|
+
return current === this._head ? void 0 : [current.key, current.value];
|
|
3066
|
+
}
|
|
3067
|
+
/**
|
|
3068
|
+
* Remove and return the first (smallest key) entry.
|
|
3069
|
+
|
|
3070
|
+
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
|
|
3074
|
+
|
|
3075
|
+
|
|
3076
|
+
|
|
3077
|
+
* @example
|
|
3078
|
+
* // Remove and return smallest
|
|
3079
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
|
|
3080
|
+
* console.log(sl.pollFirst()); // [1, 'a'];
|
|
3081
|
+
* console.log(sl.size); // 2;
|
|
3082
|
+
*/
|
|
3083
|
+
pollFirst() {
|
|
3084
|
+
const entry = this.first();
|
|
3085
|
+
if (!entry) return void 0;
|
|
3086
|
+
this.delete(entry[0]);
|
|
3087
|
+
return entry;
|
|
3088
|
+
}
|
|
3089
|
+
/**
|
|
3090
|
+
* Remove and return the last (largest key) entry.
|
|
3091
|
+
|
|
3092
|
+
|
|
3093
|
+
|
|
3094
|
+
|
|
3095
|
+
|
|
3096
|
+
|
|
3097
|
+
|
|
3098
|
+
|
|
3099
|
+
* @example
|
|
3100
|
+
* // Remove and return largest
|
|
3101
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
|
|
3102
|
+
* console.log(sl.pollLast()); // [3, 'c'];
|
|
3103
|
+
* console.log(sl.size); // 2;
|
|
3104
|
+
*/
|
|
3105
|
+
pollLast() {
|
|
3106
|
+
const entry = this.last();
|
|
3107
|
+
if (!entry) return void 0;
|
|
3108
|
+
this.delete(entry[0]);
|
|
3109
|
+
return entry;
|
|
3110
|
+
}
|
|
3111
|
+
/**
|
|
3112
|
+
* Least entry ≥ key, or `undefined`.
|
|
3113
|
+
|
|
3114
|
+
|
|
3115
|
+
|
|
3116
|
+
|
|
3117
|
+
|
|
3118
|
+
|
|
3119
|
+
|
|
3120
|
+
|
|
3121
|
+
|
|
3122
|
+
|
|
3123
|
+
|
|
3124
|
+
* @example
|
|
3125
|
+
* // Least entry ≥ key
|
|
3126
|
+
* const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
|
|
3127
|
+
* console.log(sl.ceiling(15)); // [20, 'b'];
|
|
3128
|
+
* console.log(sl.ceiling(20)); // [20, 'b'];
|
|
3129
|
+
*/
|
|
3130
|
+
ceiling(key) {
|
|
3131
|
+
const cmp = this.#comparator;
|
|
3132
|
+
let current = this._head;
|
|
3133
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3134
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) < 0) {
|
|
2050
3135
|
current = current.forward[i];
|
|
2051
3136
|
}
|
|
2052
|
-
update[i] = current;
|
|
2053
3137
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
3138
|
+
const node = current.forward[0];
|
|
3139
|
+
return node ? [node.key, node.value] : void 0;
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* Greatest entry ≤ key, or `undefined`.
|
|
3143
|
+
|
|
3144
|
+
|
|
3145
|
+
|
|
3146
|
+
|
|
3147
|
+
|
|
3148
|
+
|
|
3149
|
+
|
|
3150
|
+
|
|
3151
|
+
|
|
3152
|
+
|
|
3153
|
+
|
|
3154
|
+
* @example
|
|
3155
|
+
* // Greatest entry ≤ key
|
|
3156
|
+
* const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
|
|
3157
|
+
* console.log(sl.floor(25)); // [20, 'b'];
|
|
3158
|
+
* console.log(sl.floor(5)); // undefined;
|
|
3159
|
+
*/
|
|
3160
|
+
floor(key) {
|
|
3161
|
+
const cmp = this.#comparator;
|
|
3162
|
+
let current = this._head;
|
|
3163
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3164
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) <= 0) {
|
|
3165
|
+
current = current.forward[i];
|
|
3166
|
+
}
|
|
2060
3167
|
}
|
|
3168
|
+
const result = current === this._head ? void 0 : current;
|
|
3169
|
+
if (result && cmp(result.key, key) <= 0) return [result.key, result.value];
|
|
3170
|
+
return void 0;
|
|
2061
3171
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
3172
|
+
/**
|
|
3173
|
+
* Least entry strictly > key, or `undefined`.
|
|
3174
|
+
|
|
3175
|
+
|
|
3176
|
+
|
|
3177
|
+
|
|
3178
|
+
|
|
3179
|
+
|
|
3180
|
+
|
|
3181
|
+
|
|
3182
|
+
* @example
|
|
3183
|
+
* // Strictly greater entry
|
|
3184
|
+
* const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
|
|
3185
|
+
* console.log(sl.higher(15)); // [20, 'b'];
|
|
3186
|
+
* console.log(sl.higher(30)); // undefined;
|
|
3187
|
+
*/
|
|
3188
|
+
higher(key) {
|
|
3189
|
+
const cmp = this.#comparator;
|
|
3190
|
+
let current = this._head;
|
|
3191
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3192
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) <= 0) {
|
|
2066
3193
|
current = current.forward[i];
|
|
2067
3194
|
}
|
|
2068
3195
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
3196
|
+
const node = current.forward[0];
|
|
3197
|
+
return node ? [node.key, node.value] : void 0;
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Greatest entry strictly < key, or `undefined`.
|
|
3201
|
+
|
|
3202
|
+
|
|
3203
|
+
|
|
3204
|
+
|
|
3205
|
+
|
|
3206
|
+
|
|
3207
|
+
|
|
3208
|
+
|
|
3209
|
+
* @example
|
|
3210
|
+
* // Strictly less entry
|
|
3211
|
+
* const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
|
|
3212
|
+
* console.log(sl.lower(25)); // [20, 'b'];
|
|
3213
|
+
* console.log(sl.lower(10)); // undefined;
|
|
3214
|
+
*/
|
|
3215
|
+
lower(key) {
|
|
3216
|
+
const cmp = this.#comparator;
|
|
3217
|
+
let current = this._head;
|
|
3218
|
+
let result;
|
|
3219
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3220
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) < 0) {
|
|
3221
|
+
current = current.forward[i];
|
|
3222
|
+
}
|
|
3223
|
+
if (current !== this._head && cmp(current.key, key) < 0) {
|
|
3224
|
+
result = current;
|
|
3225
|
+
}
|
|
2072
3226
|
}
|
|
2073
|
-
return void 0;
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
3227
|
+
return result ? [result.key, result.value] : void 0;
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Returns entries within the given key range.
|
|
3231
|
+
|
|
3232
|
+
|
|
3233
|
+
|
|
3234
|
+
|
|
3235
|
+
|
|
3236
|
+
|
|
3237
|
+
|
|
3238
|
+
|
|
3239
|
+
|
|
3240
|
+
|
|
3241
|
+
|
|
3242
|
+
* @example
|
|
3243
|
+
* // Find entries in a range
|
|
3244
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]);
|
|
3245
|
+
* const result = sl.rangeSearch([2, 4]);
|
|
3246
|
+
* console.log(result); // [[2, 'b'], [3, 'c'], [4, 'd']];
|
|
3247
|
+
*/
|
|
3248
|
+
rangeSearch(range, options = {}) {
|
|
3249
|
+
const { lowInclusive = true, highInclusive = true } = options;
|
|
3250
|
+
const [low, high] = range;
|
|
3251
|
+
const cmp = this.#comparator;
|
|
3252
|
+
const out = [];
|
|
3253
|
+
let current = this._head;
|
|
3254
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3255
|
+
while (current.forward[i] && cmp(current.forward[i].key, low) < 0) {
|
|
2083
3256
|
current = current.forward[i];
|
|
2084
3257
|
}
|
|
2085
|
-
update[i] = current;
|
|
2086
3258
|
}
|
|
2087
3259
|
current = current.forward[0];
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
while (this.level > 0 && !this.head.forward[this.level - 1]) {
|
|
2096
|
-
this._level--;
|
|
3260
|
+
while (current) {
|
|
3261
|
+
const cmpHigh = cmp(current.key, high);
|
|
3262
|
+
if (cmpHigh > 0) break;
|
|
3263
|
+
if (cmpHigh === 0 && !highInclusive) break;
|
|
3264
|
+
const cmpLow = cmp(current.key, low);
|
|
3265
|
+
if (cmpLow > 0 || cmpLow === 0 && lowInclusive) {
|
|
3266
|
+
out.push([current.key, current.value]);
|
|
2097
3267
|
}
|
|
2098
|
-
|
|
3268
|
+
current = current.forward[0];
|
|
2099
3269
|
}
|
|
2100
|
-
return
|
|
3270
|
+
return out;
|
|
2101
3271
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
3272
|
+
// ─── Functional (overrides) ──────────────────────────────────
|
|
3273
|
+
/**
|
|
3274
|
+
* Creates a new SkipList with entries transformed by callback.
|
|
3275
|
+
|
|
3276
|
+
|
|
3277
|
+
|
|
3278
|
+
|
|
3279
|
+
|
|
3280
|
+
|
|
3281
|
+
|
|
3282
|
+
|
|
3283
|
+
* @example
|
|
3284
|
+
* // Transform entries
|
|
3285
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
|
|
3286
|
+
* const mapped = sl.map((v, k) => [k, v?.toUpperCase()] as [number, string]);
|
|
3287
|
+
* console.log([...mapped.values()]); // ['A', 'B'];
|
|
3288
|
+
*/
|
|
3289
|
+
map(callback, options) {
|
|
3290
|
+
const out = new _SkipList([], options ?? {});
|
|
3291
|
+
let i = 0;
|
|
3292
|
+
for (const [k, v] of this) {
|
|
3293
|
+
const [nk, nv] = callback(v, k, i++, this);
|
|
3294
|
+
out.set(nk, nv);
|
|
3295
|
+
}
|
|
3296
|
+
return out;
|
|
3297
|
+
}
|
|
3298
|
+
/**
|
|
3299
|
+
* Creates a new SkipList with entries that pass the predicate.
|
|
3300
|
+
|
|
3301
|
+
|
|
3302
|
+
|
|
3303
|
+
|
|
3304
|
+
|
|
3305
|
+
|
|
3306
|
+
|
|
3307
|
+
|
|
3308
|
+
* @example
|
|
3309
|
+
* // Filter entries
|
|
3310
|
+
* const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
|
|
3311
|
+
* const result = sl.filter((v, k) => k > 1);
|
|
3312
|
+
* console.log(result.size); // 2;
|
|
3313
|
+
*/
|
|
3314
|
+
filter(callbackfn, thisArg) {
|
|
3315
|
+
const out = new _SkipList([], {
|
|
3316
|
+
comparator: this.#isDefaultComparator ? void 0 : this.#comparator,
|
|
3317
|
+
maxLevel: this._maxLevel,
|
|
3318
|
+
probability: this._probability
|
|
3319
|
+
});
|
|
3320
|
+
let i = 0;
|
|
3321
|
+
for (const [k, v] of this) {
|
|
3322
|
+
const ok = callbackfn.call(thisArg, v, k, i++, this);
|
|
3323
|
+
if (ok) out.set(k, v);
|
|
3324
|
+
}
|
|
3325
|
+
return out;
|
|
3326
|
+
}
|
|
3327
|
+
// ─── Iterator (required by IterableEntryBase) ────────────────
|
|
3328
|
+
_getIterator() {
|
|
3329
|
+
const head = this._head;
|
|
3330
|
+
return (function* () {
|
|
3331
|
+
let node = head.forward[0];
|
|
3332
|
+
while (node) {
|
|
3333
|
+
yield [node.key, node.value];
|
|
3334
|
+
node = node.forward[0];
|
|
3335
|
+
}
|
|
3336
|
+
})();
|
|
3337
|
+
}
|
|
3338
|
+
// ─── Internal helpers ────────────────────────────────────────
|
|
3339
|
+
/**
|
|
3340
|
+
* Finds the update array (predecessors at each level) for a given key.
|
|
3341
|
+
*/
|
|
3342
|
+
_findUpdate(key) {
|
|
3343
|
+
const cmp = this.#comparator;
|
|
3344
|
+
const update = new Array(this._maxLevel).fill(this._head);
|
|
3345
|
+
let current = this._head;
|
|
3346
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3347
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) < 0) {
|
|
2106
3348
|
current = current.forward[i];
|
|
2107
3349
|
}
|
|
3350
|
+
update[i] = current;
|
|
2108
3351
|
}
|
|
2109
|
-
|
|
2110
|
-
return nextNode ? nextNode.value : void 0;
|
|
3352
|
+
return update;
|
|
2111
3353
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
3354
|
+
/**
|
|
3355
|
+
* Finds the node for a given key, or undefined.
|
|
3356
|
+
*/
|
|
3357
|
+
_findNode(key) {
|
|
3358
|
+
const cmp = this.#comparator;
|
|
3359
|
+
let current = this._head;
|
|
3360
|
+
for (let i = this._level - 1; i >= 0; i--) {
|
|
3361
|
+
while (current.forward[i] && cmp(current.forward[i].key, key) < 0) {
|
|
2117
3362
|
current = current.forward[i];
|
|
2118
3363
|
}
|
|
2119
|
-
if (current.key < key) {
|
|
2120
|
-
lastLess = current;
|
|
2121
|
-
}
|
|
2122
3364
|
}
|
|
2123
|
-
|
|
3365
|
+
const candidate = current.forward[0];
|
|
3366
|
+
if (candidate && cmp(candidate.key, key) === 0) return candidate;
|
|
3367
|
+
return void 0;
|
|
2124
3368
|
}
|
|
2125
3369
|
_randomLevel() {
|
|
2126
3370
|
let level = 1;
|
|
2127
|
-
while (Math.random() < this.
|
|
3371
|
+
while (Math.random() < this._probability && level < this._maxLevel) {
|
|
2128
3372
|
level++;
|
|
2129
3373
|
}
|
|
2130
3374
|
return level;
|