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