min-heap-typed 2.0.5 → 2.1.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.
Files changed (101) hide show
  1. package/dist/data-structures/base/iterable-element-base.d.ts +186 -83
  2. package/dist/data-structures/base/iterable-element-base.js +149 -107
  3. package/dist/data-structures/base/iterable-entry-base.d.ts +95 -119
  4. package/dist/data-structures/base/iterable-entry-base.js +59 -116
  5. package/dist/data-structures/base/linear-base.d.ts +250 -192
  6. package/dist/data-structures/base/linear-base.js +137 -274
  7. package/dist/data-structures/binary-tree/avl-tree-counter.d.ts +126 -158
  8. package/dist/data-structures/binary-tree/avl-tree-counter.js +171 -205
  9. package/dist/data-structures/binary-tree/avl-tree-multi-map.d.ts +100 -69
  10. package/dist/data-structures/binary-tree/avl-tree-multi-map.js +135 -87
  11. package/dist/data-structures/binary-tree/avl-tree.d.ts +138 -149
  12. package/dist/data-structures/binary-tree/avl-tree.js +208 -195
  13. package/dist/data-structures/binary-tree/binary-tree.d.ts +476 -632
  14. package/dist/data-structures/binary-tree/binary-tree.js +598 -869
  15. package/dist/data-structures/binary-tree/bst.d.ts +258 -306
  16. package/dist/data-structures/binary-tree/bst.js +505 -481
  17. package/dist/data-structures/binary-tree/red-black-tree.d.ts +107 -179
  18. package/dist/data-structures/binary-tree/red-black-tree.js +114 -209
  19. package/dist/data-structures/binary-tree/tree-counter.d.ts +132 -154
  20. package/dist/data-structures/binary-tree/tree-counter.js +172 -203
  21. package/dist/data-structures/binary-tree/tree-multi-map.d.ts +72 -69
  22. package/dist/data-structures/binary-tree/tree-multi-map.js +105 -85
  23. package/dist/data-structures/graph/abstract-graph.d.ts +238 -233
  24. package/dist/data-structures/graph/abstract-graph.js +267 -237
  25. package/dist/data-structures/graph/directed-graph.d.ts +108 -224
  26. package/dist/data-structures/graph/directed-graph.js +146 -233
  27. package/dist/data-structures/graph/map-graph.d.ts +49 -55
  28. package/dist/data-structures/graph/map-graph.js +56 -59
  29. package/dist/data-structures/graph/undirected-graph.d.ts +103 -146
  30. package/dist/data-structures/graph/undirected-graph.js +129 -149
  31. package/dist/data-structures/hash/hash-map.d.ts +164 -338
  32. package/dist/data-structures/hash/hash-map.js +270 -457
  33. package/dist/data-structures/heap/heap.d.ts +214 -289
  34. package/dist/data-structures/heap/heap.js +340 -349
  35. package/dist/data-structures/heap/max-heap.d.ts +11 -47
  36. package/dist/data-structures/heap/max-heap.js +11 -66
  37. package/dist/data-structures/heap/min-heap.d.ts +12 -47
  38. package/dist/data-structures/heap/min-heap.js +11 -66
  39. package/dist/data-structures/linked-list/doubly-linked-list.d.ts +231 -347
  40. package/dist/data-structures/linked-list/doubly-linked-list.js +368 -494
  41. package/dist/data-structures/linked-list/singly-linked-list.d.ts +261 -310
  42. package/dist/data-structures/linked-list/singly-linked-list.js +447 -466
  43. package/dist/data-structures/linked-list/skip-linked-list.d.ts +0 -107
  44. package/dist/data-structures/linked-list/skip-linked-list.js +0 -100
  45. package/dist/data-structures/priority-queue/max-priority-queue.d.ts +12 -56
  46. package/dist/data-structures/priority-queue/max-priority-queue.js +11 -78
  47. package/dist/data-structures/priority-queue/min-priority-queue.d.ts +11 -57
  48. package/dist/data-structures/priority-queue/min-priority-queue.js +10 -79
  49. package/dist/data-structures/priority-queue/priority-queue.d.ts +2 -61
  50. package/dist/data-structures/priority-queue/priority-queue.js +8 -83
  51. package/dist/data-structures/queue/deque.d.ts +227 -254
  52. package/dist/data-structures/queue/deque.js +309 -348
  53. package/dist/data-structures/queue/queue.d.ts +180 -201
  54. package/dist/data-structures/queue/queue.js +265 -248
  55. package/dist/data-structures/stack/stack.d.ts +124 -102
  56. package/dist/data-structures/stack/stack.js +181 -125
  57. package/dist/data-structures/trie/trie.d.ts +164 -165
  58. package/dist/data-structures/trie/trie.js +189 -172
  59. package/dist/interfaces/binary-tree.d.ts +56 -6
  60. package/dist/interfaces/graph.d.ts +16 -0
  61. package/dist/types/data-structures/base/base.d.ts +1 -1
  62. package/dist/types/data-structures/graph/abstract-graph.d.ts +4 -0
  63. package/dist/types/utils/utils.d.ts +1 -0
  64. package/dist/utils/utils.d.ts +1 -1
  65. package/dist/utils/utils.js +2 -1
  66. package/package.json +2 -2
  67. package/src/data-structures/base/iterable-element-base.ts +238 -115
  68. package/src/data-structures/base/iterable-entry-base.ts +96 -120
  69. package/src/data-structures/base/linear-base.ts +271 -277
  70. package/src/data-structures/binary-tree/avl-tree-counter.ts +198 -216
  71. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +192 -101
  72. package/src/data-structures/binary-tree/avl-tree.ts +239 -206
  73. package/src/data-structures/binary-tree/binary-tree.ts +664 -893
  74. package/src/data-structures/binary-tree/bst.ts +568 -570
  75. package/src/data-structures/binary-tree/red-black-tree.ts +161 -222
  76. package/src/data-structures/binary-tree/tree-counter.ts +199 -218
  77. package/src/data-structures/binary-tree/tree-multi-map.ts +131 -97
  78. package/src/data-structures/graph/abstract-graph.ts +339 -264
  79. package/src/data-structures/graph/directed-graph.ts +146 -236
  80. package/src/data-structures/graph/map-graph.ts +63 -60
  81. package/src/data-structures/graph/undirected-graph.ts +129 -152
  82. package/src/data-structures/hash/hash-map.ts +274 -496
  83. package/src/data-structures/heap/heap.ts +389 -402
  84. package/src/data-structures/heap/max-heap.ts +12 -76
  85. package/src/data-structures/heap/min-heap.ts +13 -76
  86. package/src/data-structures/linked-list/doubly-linked-list.ts +426 -530
  87. package/src/data-structures/linked-list/singly-linked-list.ts +495 -517
  88. package/src/data-structures/linked-list/skip-linked-list.ts +1 -108
  89. package/src/data-structures/priority-queue/max-priority-queue.ts +12 -87
  90. package/src/data-structures/priority-queue/min-priority-queue.ts +11 -88
  91. package/src/data-structures/priority-queue/priority-queue.ts +3 -92
  92. package/src/data-structures/queue/deque.ts +381 -357
  93. package/src/data-structures/queue/queue.ts +310 -264
  94. package/src/data-structures/stack/stack.ts +217 -131
  95. package/src/data-structures/trie/trie.ts +240 -175
  96. package/src/interfaces/binary-tree.ts +240 -6
  97. package/src/interfaces/graph.ts +37 -0
  98. package/src/types/data-structures/base/base.ts +5 -5
  99. package/src/types/data-structures/graph/abstract-graph.ts +5 -0
  100. package/src/types/utils/utils.ts +2 -0
  101. package/src/utils/utils.ts +9 -14
@@ -5,15 +5,23 @@
5
5
  * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
+
8
9
  import type { ElementCallback, SinglyLinkedListOptions } from '../../types';
9
10
  import { LinearLinkedBase, LinkedListNode } from '../base/linear-base';
10
11
 
12
+ /**
13
+ * Node of a singly linked list; stores value and the next link.
14
+ * @remarks Time O(1), Space O(1)
15
+ * @template E
16
+ */
11
17
  export class SinglyLinkedListNode<E = any> extends LinkedListNode<E> {
12
18
  /**
13
- * The constructor function initializes an instance of a class with a given value and sets the next property to undefined.
14
- * @param {E} value - The "value" parameter is of type E, which means it can be any data type. It represents the value that
15
- * will be stored in the node of a linked list.
19
+ * Create a list node.
20
+ * @remarks Time O(1), Space O(1)
21
+ * @param value - Element value to store.
22
+ * @returns New node instance.
16
23
  */
24
+
17
25
  constructor(value: E) {
18
26
  super(value);
19
27
  this._value = value;
@@ -22,22 +30,38 @@ export class SinglyLinkedListNode<E = any> extends LinkedListNode<E> {
22
30
 
23
31
  protected override _next: SinglyLinkedListNode<E> | undefined;
24
32
 
33
+ /**
34
+ * Get the next node.
35
+ * @remarks Time O(1), Space O(1)
36
+ * @returns Next node or undefined.
37
+ */
38
+
25
39
  override get next(): SinglyLinkedListNode<E> | undefined {
26
40
  return this._next;
27
41
  }
28
42
 
43
+ /**
44
+ * Set the next node.
45
+ * @remarks Time O(1), Space O(1)
46
+ * @param value - Next node or undefined.
47
+ * @returns void
48
+ */
49
+
29
50
  override set next(value: SinglyLinkedListNode<E> | undefined) {
30
51
  this._next = value;
31
52
  }
32
53
  }
33
54
 
34
55
  /**
56
+ * Singly linked list with O(1) push/pop-like ends operations and linear scans.
57
+ * @remarks Time O(1), Space O(1)
58
+ * @template E
59
+ * @template R
35
60
  * 1. Node Structure: Each node contains three parts: a data field, a pointer (or reference) to the previous node, and a pointer to the next node. This structure allows traversal of the linked list in both directions.
36
61
  * 2. Bidirectional Traversal: Unlike doubly linked lists, singly linked lists can be easily traversed forwards but not backwards.
37
62
  * 3. No Centralized Index: Unlike arrays, elements in a linked list are not stored contiguously, so there is no centralized index. Accessing elements in a linked list typically requires traversing from the head or tail node.
38
63
  * 4. High Efficiency in Insertion and Deletion: Adding or removing elements in a linked list does not require moving other elements, making these operations more efficient than in arrays.
39
64
  * Caution: Although our linked list classes provide methods such as at, setAt, addAt, and indexOf that are based on array indices, their time complexity, like that of the native Array.lastIndexOf, is 𝑂(𝑛). If you need to use these methods frequently, you might want to consider other data structures, such as Deque or Queue (designed for random access). Similarly, since the native Array.shift method has a time complexity of 𝑂(𝑛), using an array to simulate a queue can be inefficient. In such cases, you should use Queue or Deque, as these data structures leverage deferred array rearrangement, effectively reducing the average time complexity to 𝑂(1).
40
- *
41
65
  * @example
42
66
  * // implementation of a basic text editor
43
67
  * class TextEditor {
@@ -106,75 +130,116 @@ export class SinglyLinkedListNode<E = any> extends LinkedListNode<E> {
106
130
  * console.log(editor.getText()); // 'Haello'
107
131
  */
108
132
  export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, SinglyLinkedListNode<E>> {
133
+ protected _equals: (a: E, b: E) => boolean = Object.is as unknown as (a: E, b: E) => boolean;
134
+
135
+ /**
136
+ * Create a SinglyLinkedList and optionally bulk-insert elements.
137
+ * @remarks Time O(N), Space O(N)
138
+ * @param [elements] - Iterable of elements or nodes (or raw records if toElementFn is provided).
139
+ * @param [options] - Options such as maxLen and toElementFn.
140
+ * @returns New SinglyLinkedList instance.
141
+ */
142
+
109
143
  constructor(
110
144
  elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>> = [],
111
145
  options?: SinglyLinkedListOptions<E, R>
112
146
  ) {
113
147
  super(options);
114
-
115
- if (options) {
116
- }
117
-
118
148
  this.pushMany(elements);
119
149
  }
120
150
 
121
151
  protected _head: SinglyLinkedListNode<E> | undefined;
122
152
 
153
+ /**
154
+ * Get the head node.
155
+ * @remarks Time O(1), Space O(1)
156
+ * @returns Head node or undefined.
157
+ */
158
+
123
159
  get head(): SinglyLinkedListNode<E> | undefined {
124
160
  return this._head;
125
161
  }
126
162
 
127
163
  protected _tail: SinglyLinkedListNode<E> | undefined;
128
164
 
165
+ /**
166
+ * Get the tail node.
167
+ * @remarks Time O(1), Space O(1)
168
+ * @returns Tail node or undefined.
169
+ */
170
+
129
171
  get tail(): SinglyLinkedListNode<E> | undefined {
130
172
  return this._tail;
131
173
  }
132
174
 
175
+ protected _length = 0;
176
+
177
+ /**
178
+ * Get the number of elements.
179
+ * @remarks Time O(1), Space O(1)
180
+ * @returns Current length.
181
+ */
182
+
183
+ get length(): number {
184
+ return this._length;
185
+ }
186
+
187
+ /**
188
+ * Get the first element value.
189
+ * @remarks Time O(1), Space O(1)
190
+ * @returns First element or undefined.
191
+ */
192
+
133
193
  get first(): E | undefined {
134
194
  return this.head?.value;
135
195
  }
136
196
 
197
+ /**
198
+ * Get the last element value.
199
+ * @remarks Time O(1), Space O(1)
200
+ * @returns Last element or undefined.
201
+ */
202
+
137
203
  get last(): E | undefined {
138
204
  return this.tail?.value;
139
205
  }
140
206
 
141
- protected _length: number = 0;
142
-
143
- get length(): number {
144
- return this._length;
145
- }
146
-
147
207
  /**
148
- * Time Complexity: O(n)
149
- * Space Complexity: O(n)
150
- *
151
- * The `fromArray` function creates a new SinglyLinkedList instance and populates it with the elements from the given
152
- * array.
153
- * @param {E[]} data - The `data` parameter is an array of elements of type `E`.
154
- * @returns The `fromArray` function returns a `SinglyLinkedList` object.
208
+ * Create a new list from an iterable of elements.
209
+ * @remarks Time O(N), Space O(N)
210
+ * @template E
211
+ * @template R
212
+ * @template S
213
+ * @param this - The constructor (subclass) to instantiate.
214
+ * @param data - Iterable of elements to insert.
215
+ * @param [options] - Options forwarded to the constructor.
216
+ * @returns A new list populated with the iterable's elements.
155
217
  */
156
- static fromArray<E>(data: E[]) {
157
- const singlyLinkedList = new SinglyLinkedList<E>();
158
- for (const item of data) {
159
- singlyLinkedList.push(item);
160
- }
161
- return singlyLinkedList;
218
+
219
+ static from<E, R = any, S extends SinglyLinkedList<E, R> = SinglyLinkedList<E, R>>(
220
+ this: new (
221
+ elements?: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>,
222
+ options?: SinglyLinkedListOptions<E, R>
223
+ ) => S,
224
+ data: Iterable<E>,
225
+ options?: SinglyLinkedListOptions<E, R>
226
+ ): S {
227
+ const list = new this([], options);
228
+ for (const x of data) list.push(x);
229
+ return list;
162
230
  }
163
231
 
164
232
  /**
165
- * Time Complexity: O(1)
166
- * Space Complexity: O(1)
167
- *
168
- * The `push` function adds a new element or node to the end of a singly linked list.
169
- * @param {E | SinglyLinkedListNode<E>} elementOrNode - The `elementOrNode` parameter in the `push`
170
- * method can accept either an element of type `E` or a `SinglyLinkedListNode<E>` object.
171
- * @returns The `push` method is returning a boolean value, specifically `true`.
233
+ * Append an element/node to the tail.
234
+ * @remarks Time O(1), Space O(1)
235
+ * @param elementOrNode - Element or node to append.
236
+ * @returns True when appended.
172
237
  */
238
+
173
239
  push(elementOrNode: E | SinglyLinkedListNode<E>): boolean {
174
240
  const newNode = this._ensureNode(elementOrNode);
175
241
  if (!this.head) {
176
- this._head = newNode;
177
- this._tail = newNode;
242
+ this._head = this._tail = newNode;
178
243
  } else {
179
244
  this.tail!.next = newNode;
180
245
  this._tail = newNode;
@@ -185,13 +250,11 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
185
250
  }
186
251
 
187
252
  /**
188
- * Time Complexity: O(n)
189
- * Space Complexity: O(1)
190
- *
191
- * The `pop` function removes and returns the value of the last element in a linked list.
192
- * @returns The method is returning the value of the element that is being popped from the end of the
193
- * list.
253
+ * Remove and return the tail element.
254
+ * @remarks Time O(N), Space O(1)
255
+ * @returns Removed element or undefined.
194
256
  */
257
+
195
258
  pop(): E | undefined {
196
259
  if (!this.head) return undefined;
197
260
  if (this.head === this.tail) {
@@ -201,11 +264,8 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
201
264
  this._length--;
202
265
  return value;
203
266
  }
204
-
205
267
  let current = this.head;
206
- while (current.next !== this.tail) {
207
- current = current.next!;
208
- }
268
+ while (current.next !== this.tail) current = current.next!;
209
269
  const value = this.tail!.value;
210
270
  current.next = undefined;
211
271
  this._tail = current;
@@ -214,36 +274,31 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
214
274
  }
215
275
 
216
276
  /**
217
- * Time Complexity: O(1)
218
- * Space Complexity: O(1)
219
- *
220
- * The `shift()` function removes and returns the value of the first element in a linked list.
221
- * @returns The value of the removed node.
277
+ * Remove and return the head element.
278
+ * @remarks Time O(1), Space O(1)
279
+ * @returns Removed element or undefined.
222
280
  */
281
+
223
282
  shift(): E | undefined {
224
283
  if (!this.head) return undefined;
225
- const removedNode = this.head;
284
+ const removed = this.head;
226
285
  this._head = this.head.next;
286
+ if (!this._head) this._tail = undefined;
227
287
  this._length--;
228
- return removedNode.value;
288
+ return removed.value;
229
289
  }
230
290
 
231
291
  /**
232
- * Time Complexity: O(1)
233
- * Space Complexity: O(1)
234
- *
235
- * The unshift function adds a new element or node to the beginning of a singly linked list in
236
- * TypeScript.
237
- * @param {E | SinglyLinkedListNode<E>} elementOrNode - The `elementOrNode` parameter in the
238
- * `unshift` method can be either an element of type `E` or a `SinglyLinkedListNode` containing an
239
- * element of type `E`.
240
- * @returns The `unshift` method is returning a boolean value, specifically `true`.
292
+ * Prepend an element/node to the head.
293
+ * @remarks Time O(1), Space O(1)
294
+ * @param elementOrNode - Element or node to prepend.
295
+ * @returns True when prepended.
241
296
  */
297
+
242
298
  unshift(elementOrNode: E | SinglyLinkedListNode<E>): boolean {
243
299
  const newNode = this._ensureNode(elementOrNode);
244
300
  if (!this.head) {
245
- this._head = newNode;
246
- this._tail = newNode;
301
+ this._head = this._tail = newNode;
247
302
  } else {
248
303
  newNode.next = this.head;
249
304
  this._head = newNode;
@@ -253,66 +308,44 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
253
308
  }
254
309
 
255
310
  /**
256
- * Time Complexity: O(k)
257
- * Space Complexity: O(k)
258
- *
259
- * The function `pushMany` iterates over elements and pushes them into a data structure, applying a
260
- * transformation function if provided.
261
- * @param {Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>} elements - The `elements`
262
- * parameter in the `pushMany` function can accept an iterable containing elements of type `E`, `R`,
263
- * or `SinglyLinkedListNode<E>`.
264
- * @returns The `pushMany` function returns an array of boolean values indicating whether each
265
- * element was successfully pushed into the data structure.
311
+ * Append a sequence of elements/nodes.
312
+ * @remarks Time O(N), Space O(1)
313
+ * @param elements - Iterable of elements or nodes (or raw records if toElementFn is provided).
314
+ * @returns Array of per-element success flags.
266
315
  */
267
- pushMany(elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>) {
316
+
317
+ pushMany(elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>): boolean[] {
268
318
  const ans: boolean[] = [];
269
319
  for (const el of elements) {
270
- if (this.toElementFn) {
271
- ans.push(this.push(this.toElementFn(el as R)));
272
- continue;
273
- }
274
- ans.push(this.push(el as E | SinglyLinkedListNode<E>));
320
+ if (this.toElementFn) ans.push(this.push(this.toElementFn(el as R)));
321
+ else ans.push(this.push(el as E | SinglyLinkedListNode<E>));
275
322
  }
276
323
  return ans;
277
324
  }
278
325
 
279
326
  /**
280
- * Time Complexity: O(k)
281
- * Space Complexity: O(k)
282
- *
283
- * The function `unshiftMany` iterates over elements and adds them to a data structure, optionally
284
- * converting them using a provided function.
285
- * @param {Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>} elements - The `elements`
286
- * parameter in the `unshiftMany` function can accept an iterable containing elements of type `E`,
287
- * `R`, or `SinglyLinkedListNode<E>`. The function iterates over each element in the iterable and
288
- * performs an `unshift` operation on the linked list for each
289
- * @returns The `unshiftMany` function is returning an array of boolean values, where each value
290
- * represents the result of calling the `unshift` method on the current instance of the class.
327
+ * Prepend a sequence of elements/nodes.
328
+ * @remarks Time O(N), Space O(1)
329
+ * @param elements - Iterable of elements or nodes (or raw records if toElementFn is provided).
330
+ * @returns Array of per-element success flags.
291
331
  */
292
- unshiftMany(elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>) {
332
+
333
+ unshiftMany(elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>): boolean[] {
293
334
  const ans: boolean[] = [];
294
335
  for (const el of elements) {
295
- if (this.toElementFn) {
296
- ans.push(this.unshift(this.toElementFn(el as R)));
297
- continue;
298
- }
299
- ans.push(this.unshift(el as E | SinglyLinkedListNode<E>));
336
+ if (this.toElementFn) ans.push(this.unshift(this.toElementFn(el as R)));
337
+ else ans.push(this.unshift(el as E | SinglyLinkedListNode<E>));
300
338
  }
301
339
  return ans;
302
340
  }
303
341
 
304
342
  /**
305
- * Time Complexity: O(n)
306
- * Space Complexity: O(1)
307
- *
308
- * This function searches for a specific element in a singly linked list based on a given node or
309
- * predicate.
310
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)} elementNodeOrPredicate
311
- * elementNodeOrPredicate - The `elementNodeOrPredicate` parameter in the `get` method can be one of
312
- * the following types:
313
- * @returns The `get` method returns the value of the first node in the singly linked list that
314
- * satisfies the provided predicate function. If no such node is found, it returns `undefined`.
343
+ * Find the first value matching a predicate (by node).
344
+ * @remarks Time O(N), Space O(1)
345
+ * @param elementNodeOrPredicate - Element, node, or node predicate to match.
346
+ * @returns Matched value or undefined.
315
347
  */
348
+
316
349
  search(
317
350
  elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)
318
351
  ): E | undefined {
@@ -326,37 +359,26 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
326
359
  }
327
360
 
328
361
  /**
329
- * Time Complexity: O(n)
330
- * Space Complexity: O(1)
331
- *
332
- * The function `at` returns the value at a specified index in a linked list, or undefined if the index is out of range.
333
- * @param {number} index - The index parameter is a number that represents the position of the element we want to
334
- * retrieve from the list.
335
- * @returns The method `at(index: number): E | undefined` returns the value at the specified index in the linked list, or
336
- * `undefined` if the index is out of bounds.
362
+ * Get the element at a given index.
363
+ * @remarks Time O(N), Space O(1)
364
+ * @param index - Zero-based index.
365
+ * @returns Element or undefined.
337
366
  */
367
+
338
368
  at(index: number): E | undefined {
339
369
  if (index < 0 || index >= this._length) return undefined;
340
370
  let current = this.head;
341
- for (let i = 0; i < index; i++) {
342
- current = current!.next;
343
- }
371
+ for (let i = 0; i < index; i++) current = current!.next;
344
372
  return current!.value;
345
373
  }
346
374
 
347
375
  /**
348
- * Time Complexity: O(1)
349
- * Space Complexity: O(1)
350
- *
351
- * The function `isNode` in TypeScript checks if the input is an instance of `SinglyLinkedListNode`.
352
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)} elementNodeOrPredicate
353
- * elementNodeOrPredicate - The `elementNodeOrPredicate` parameter in the `isNode` function can be
354
- * one of the following types:
355
- * @returns The `isNode` function is checking if the `elementNodeOrPredicate` parameter is an
356
- * instance of `SinglyLinkedListNode<E>`. If it is, the function returns `true`, indicating that the
357
- * parameter is a `SinglyLinkedListNode<E>`. If it is not an instance of `SinglyLinkedListNode<E>`,
358
- * the function returns `false`.
376
+ * Type guard: check whether the input is a SinglyLinkedListNode.
377
+ * @remarks Time O(1), Space O(1)
378
+ * @param elementNodeOrPredicate - Element, node, or predicate.
379
+ * @returns True if the value is a SinglyLinkedListNode.
359
380
  */
381
+
360
382
  isNode(
361
383
  elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)
362
384
  ): elementNodeOrPredicate is SinglyLinkedListNode<E> {
@@ -364,164 +386,113 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
364
386
  }
365
387
 
366
388
  /**
367
- * Time Complexity: O(n)
368
- * Space Complexity: O(1)
369
- *
370
- * The function `getNodeAt` returns the node at a given index in a singly linked list.
371
- * @param {number} index - The `index` parameter is a number that represents the position of the node we want to
372
- * retrieve from the linked list. It indicates the zero-based index of the node we want to access.
373
- * @returns The method `getNodeAt(index: number)` returns a `SinglyLinkedListNode<E>` object if the node at the
374
- * specified index exists, or `undefined` if the index is out of bounds.
389
+ * Get the node reference at a given index.
390
+ * @remarks Time O(N), Space O(1)
391
+ * @param index - Zero-based index.
392
+ * @returns Node or undefined.
375
393
  */
394
+
376
395
  getNodeAt(index: number): SinglyLinkedListNode<E> | undefined {
396
+ if (index < 0 || index >= this._length) return undefined;
377
397
  let current = this.head;
378
- for (let i = 0; i < index; i++) {
379
- current = current!.next;
380
- }
398
+ for (let i = 0; i < index; i++) current = current!.next;
381
399
  return current;
382
400
  }
383
401
 
384
402
  /**
385
- * Time Complexity: O(n)
386
- * Space Complexity: O(1)
387
- *
388
- * The `deleteAt` function removes an element at a specified index from a linked list and returns the removed element.
389
- * @param {number} index - The index parameter represents the position of the element that needs to be deleted in the
390
- * data structure. It is of type number.
391
- * @returns The method `deleteAt` returns the value of the node that was deleted, or `undefined` if the index is out of
392
- * bounds.
403
+ * Delete the element at an index.
404
+ * @remarks Time O(N), Space O(1)
405
+ * @param index - Zero-based index.
406
+ * @returns Removed element or undefined.
393
407
  */
394
- deleteAt(index: number): E | undefined {
395
- if (index < 0 || index >= this._length) return;
396
- let deleted: E | undefined;
397
- if (index === 0) {
398
- deleted = this.first;
399
- this.shift();
400
- return deleted;
401
- }
402
408
 
403
- const targetNode = this.getNodeAt(index);
404
- const prevNode = this._getPrevNode(targetNode!);
405
-
406
- if (prevNode && targetNode) {
407
- deleted = targetNode.value;
408
- prevNode.next = targetNode.next;
409
- if (targetNode === this.tail) this._tail = prevNode;
410
- this._length--;
411
- return deleted;
412
- }
413
-
414
- return;
409
+ deleteAt(index: number): E | undefined {
410
+ if (index < 0 || index >= this._length) return undefined;
411
+ if (index === 0) return this.shift();
412
+ const targetNode = this.getNodeAt(index)!;
413
+ const prevNode = this._getPrevNode(targetNode)!;
414
+ const value = targetNode.value;
415
+ prevNode.next = targetNode.next;
416
+ if (targetNode === this.tail) this._tail = prevNode;
417
+ this._length--;
418
+ return value;
415
419
  }
416
420
 
417
421
  /**
418
- * Time Complexity: O(n)
419
- * Space Complexity: O(1)
420
- *
421
- * The delete function removes a node with a specific value from a singly linked list.
422
- * @param {E | SinglyLinkedListNode<E>} elementOrNode - The `elementOrNode` parameter can accept either a value of type `E`
423
- * or a `SinglyLinkedListNode<E>` object.
424
- * @returns The `delete` method returns a boolean value. It returns `true` if the value or node is found and
425
- * successfully deleted from the linked list, and `false` if the value or node is not found in the linked list.
422
+ * Delete the first match by value/node.
423
+ * @remarks Time O(N), Space O(1)
424
+ * @param [elementOrNode] - Element or node to remove; if omitted/undefined, nothing happens.
425
+ * @returns True if removed.
426
426
  */
427
+
427
428
  delete(elementOrNode: E | SinglyLinkedListNode<E> | undefined): boolean {
428
429
  if (elementOrNode === undefined || !this.head) return false;
429
-
430
430
  const node = this.isNode(elementOrNode) ? elementOrNode : this.getNode(elementOrNode);
431
-
432
431
  if (!node) return false;
433
-
434
432
  const prevNode = this._getPrevNode(node);
435
433
 
436
434
  if (!prevNode) {
437
- // The node is the head
438
435
  this._head = node.next;
439
436
  if (node === this.tail) this._tail = undefined;
440
437
  } else {
441
438
  prevNode.next = node.next;
442
439
  if (node === this.tail) this._tail = prevNode;
443
440
  }
444
-
445
441
  this._length--;
446
442
  return true;
447
443
  }
448
444
 
449
445
  /**
450
- * Time Complexity: O(n)
451
- * Space Complexity: O(1)
452
- *
453
- * The `addAt` function inserts a new element or node at a specified index in a singly linked list.
454
- * @param {number} index - The `index` parameter represents the position at which you want to add a
455
- * new element or node in the linked list. It is a number that indicates the index where the new
456
- * element or node should be inserted.
457
- * @param {E | SinglyLinkedListNode<E>} newElementOrNode - The `newElementOrNode` parameter in the
458
- * `addAt` method can be either a value of type `E` or a `SinglyLinkedListNode<E>` object. This
459
- * parameter represents the element or node that you want to add to the linked list at the specified
460
- * index.
461
- * @returns The `addAt` method returns a boolean value - `true` if the element or node was
462
- * successfully added at the specified index, and `false` if the index is out of bounds.
446
+ * Insert a new element/node at an index, shifting following nodes.
447
+ * @remarks Time O(N), Space O(1)
448
+ * @param index - Zero-based index.
449
+ * @param newElementOrNode - Element or node to insert.
450
+ * @returns True if inserted.
463
451
  */
452
+
464
453
  addAt(index: number, newElementOrNode: E | SinglyLinkedListNode<E>): boolean {
465
454
  if (index < 0 || index > this._length) return false;
466
-
467
- if (index === 0) {
468
- this.unshift(newElementOrNode);
469
- return true;
470
- }
471
- if (index === this._length) {
472
- this.push(newElementOrNode);
473
- return true;
474
- }
475
-
455
+ if (index === 0) return this.unshift(newElementOrNode);
456
+ if (index === this._length) return this.push(newElementOrNode);
476
457
  const newNode = this._ensureNode(newElementOrNode);
477
- const prevNode = this.getNodeAt(index - 1);
478
- newNode.next = prevNode!.next;
479
- prevNode!.next = newNode;
458
+ const prevNode = this.getNodeAt(index - 1)!;
459
+ newNode.next = prevNode.next;
460
+ prevNode.next = newNode;
480
461
  this._length++;
481
462
  return true;
482
463
  }
483
464
 
484
465
  /**
485
- * Time Complexity: O(n)
486
- * Space Complexity: O(1)
487
- *
488
- * The function setAt(index, value) updates the value at a specified index in a data structure if the
489
- * index exists.
490
- * @param {number} index - The `index` parameter in the `setAt` method refers to the position in the
491
- * data structure where you want to set a new value.
492
- * @param {E} value - The `value` parameter in the `setAt` method represents the new value that you
493
- * want to set at the specified index in the data structure.
494
- * @returns The `setAt` method returns a boolean value - `true` if the value at the specified index
495
- * is successfully updated, and `false` if the index is out of bounds (i.e., the node at that index
496
- * does not exist).
466
+ * Set the element value at an index.
467
+ * @remarks Time O(N), Space O(1)
468
+ * @param index - Zero-based index.
469
+ * @param value - New value.
470
+ * @returns True if updated.
497
471
  */
472
+
498
473
  setAt(index: number, value: E): boolean {
499
474
  const node = this.getNodeAt(index);
500
- if (node) {
501
- node.value = value;
502
- return true;
503
- }
504
- return false;
475
+ if (!node) return false;
476
+ node.value = value;
477
+ return true;
505
478
  }
506
479
 
507
480
  /**
508
- * Time Complexity: O(1)
509
- * Space Complexity: O(1)
510
- *
511
- * The function checks if the length of a data structure is equal to zero and returns a boolean value indicating
512
- * whether it is empty or not.
513
- * @returns A boolean value indicating whether the length of the object is equal to 0.
481
+ * Check whether the list is empty.
482
+ * @remarks Time O(1), Space O(1)
483
+ * @returns True if length is 0.
514
484
  */
485
+
515
486
  isEmpty(): boolean {
516
487
  return this._length === 0;
517
488
  }
518
489
 
519
490
  /**
520
- * Time Complexity: O(1)
521
- * Space Complexity: O(1)
522
- *
523
- * The `clear` function resets the linked list by setting the head, tail, and length to undefined and 0 respectively.
491
+ * Remove all nodes and reset length.
492
+ * @remarks Time O(N), Space O(1)
493
+ * @returns void
524
494
  */
495
+
525
496
  clear(): void {
526
497
  this._head = undefined;
527
498
  this._tail = undefined;
@@ -529,43 +500,33 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
529
500
  }
530
501
 
531
502
  /**
532
- * Time Complexity: O(n)
533
- * Space Complexity: O(1)
534
- *
535
- * The `reverse` function reverses the order of the nodes in a singly linked list.
536
- * @returns The reverse() method does not return anything. It has a return type of void.
503
+ * Reverse the list in place.
504
+ * @remarks Time O(N), Space O(1)
505
+ * @returns This list.
537
506
  */
507
+
538
508
  reverse(): this {
539
509
  if (!this.head || this.head === this.tail) return this;
540
-
541
- let prev: SinglyLinkedListNode<E> | undefined = undefined;
510
+ let prev: SinglyLinkedListNode<E> | undefined;
542
511
  let current: SinglyLinkedListNode<E> | undefined = this.head;
543
- let next: SinglyLinkedListNode<E> | undefined = undefined;
544
-
512
+ let next: SinglyLinkedListNode<E> | undefined;
545
513
  while (current) {
546
514
  next = current.next;
547
515
  current.next = prev;
548
516
  prev = current;
549
517
  current = next;
550
518
  }
551
-
552
519
  [this._head, this._tail] = [this.tail!, this.head!];
553
520
  return this;
554
521
  }
555
522
 
556
523
  /**
557
- * Time Complexity: O(n)
558
- * Space Complexity: O(1)
559
- *
560
- * The function `getNode` in TypeScript searches for a node in a singly linked list based on a given
561
- * element, node, or predicate.
562
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean) | undefined} elementNodeOrPredicate
563
- * elementNodeOrPredicate - The `elementNodeOrPredicate` parameter in the `getNode` method can be one
564
- * of the following types:
565
- * @returns The `getNode` method returns either a `SinglyLinkedListNode<E>` if a matching node is
566
- * found based on the provided predicate, or it returns `undefined` if no matching node is found or
567
- * if the input parameter is `undefined`.
524
+ * Find a node by value, reference, or predicate.
525
+ * @remarks Time O(N), Space O(1)
526
+ * @param [elementNodeOrPredicate] - Element, node, or node predicate to match.
527
+ * @returns Matching node or undefined.
568
528
  */
529
+
569
530
  getNode(
570
531
  elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean) | undefined
571
532
  ): SinglyLinkedListNode<E> | undefined {
@@ -573,328 +534,264 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
573
534
  if (this.isNode(elementNodeOrPredicate)) return elementNodeOrPredicate;
574
535
  const predicate = this._ensurePredicate(elementNodeOrPredicate);
575
536
  let current = this.head;
576
-
577
537
  while (current) {
578
- if (predicate(current)) {
579
- return current;
580
- }
538
+ if (predicate(current)) return current;
581
539
  current = current.next;
582
540
  }
583
-
584
541
  return undefined;
585
542
  }
586
543
 
587
544
  /**
588
- * Time Complexity: O(n)
589
- * Space Complexity: O(1)
590
- *
591
- * The function `addBefore` in TypeScript adds a new element or node before an existing element or
592
- * node in a singly linked list.
593
- * @param {E | SinglyLinkedListNode<E>} existingElementOrNode - existingElementOrNode represents the
594
- * element or node in the linked list before which you want to add a new element or node.
595
- * @param {E | SinglyLinkedListNode<E>} newElementOrNode - The `newElementOrNode` parameter in the
596
- * `addBefore` method represents the element or node that you want to insert before the existing
597
- * element or node in the linked list. This new element can be of type `E` or a
598
- * `SinglyLinkedListNode<E>`.
599
- * @returns The `addBefore` method returns a boolean value - `true` if the new element or node was
600
- * successfully added before the existing element or node, and `false` if the operation was
601
- * unsuccessful.
545
+ * Insert a new element/node before an existing one.
546
+ * @remarks Time O(N), Space O(1)
547
+ * @param existingElementOrNode - Existing element or node.
548
+ * @param newElementOrNode - Element or node to insert.
549
+ * @returns True if inserted.
602
550
  */
551
+
603
552
  addBefore(
604
553
  existingElementOrNode: E | SinglyLinkedListNode<E>,
605
554
  newElementOrNode: E | SinglyLinkedListNode<E>
606
555
  ): boolean {
607
556
  const existingNode = this.getNode(existingElementOrNode);
608
557
  if (!existingNode) return false;
609
-
610
558
  const prevNode = this._getPrevNode(existingNode);
611
559
  const newNode = this._ensureNode(newElementOrNode);
612
560
 
613
561
  if (!prevNode) {
614
- // Add at the head
615
- this.unshift(newNode);
562
+ newNode.next = this._head;
563
+ this._head = newNode;
564
+ if (!this._tail) this._tail = newNode;
565
+ this._length++;
616
566
  } else {
617
567
  prevNode.next = newNode;
618
568
  newNode.next = existingNode;
619
569
  this._length++;
620
570
  }
621
-
622
571
  return true;
623
572
  }
624
573
 
625
574
  /**
626
- * Time Complexity: O(n)
627
- * Space Complexity: O(1)
628
- *
629
- * The `addAfter` function in TypeScript adds a new element or node after an existing element or node
630
- * in a singly linked list.
631
- * @param {E | SinglyLinkedListNode<E>} existingElementOrNode - existingElementOrNode can be either
632
- * an element of type E or a SinglyLinkedListNode of type E.
633
- * @param {E | SinglyLinkedListNode<E>} newElementOrNode - The `newElementOrNode` parameter in the
634
- * `addAfter` method represents the element or node that you want to add after the existing element
635
- * or node in a singly linked list. This parameter can be either the value of the new element or a
636
- * reference to a `SinglyLinkedListNode` containing
637
- * @returns The `addAfter` method returns a boolean value - `true` if the new element or node was
638
- * successfully added after the existing element or node, and `false` if the existing element or node
639
- * was not found.
575
+ * Insert a new element/node after an existing one.
576
+ * @remarks Time O(N), Space O(1)
577
+ * @param existingElementOrNode - Existing element or node.
578
+ * @param newElementOrNode - Element or node to insert.
579
+ * @returns True if inserted.
640
580
  */
641
- addAfter(existingElementOrNode: E | SinglyLinkedListNode<E>, newElementOrNode: E | SinglyLinkedListNode<E>): boolean {
642
- const existingNode: SinglyLinkedListNode<E> | undefined = this.getNode(existingElementOrNode);
643
-
644
- if (existingNode) {
645
- const newNode = this._ensureNode(newElementOrNode);
646
- newNode.next = existingNode.next;
647
- existingNode.next = newNode;
648
- if (existingNode === this.tail) {
649
- this._tail = newNode;
650
- }
651
- this._length++;
652
- return true;
653
- }
654
581
 
655
- return false;
582
+ addAfter(existingElementOrNode: E | SinglyLinkedListNode<E>, newElementOrNode: E | SinglyLinkedListNode<E>): boolean {
583
+ const existingNode = this.getNode(existingElementOrNode);
584
+ if (!existingNode) return false;
585
+ const newNode = this._ensureNode(newElementOrNode);
586
+ newNode.next = existingNode.next;
587
+ existingNode.next = newNode;
588
+ if (existingNode === this.tail) this._tail = newNode;
589
+ this._length++;
590
+ return true;
656
591
  }
657
592
 
658
593
  /**
659
- * Time Complexity: O(n)
660
- * Space Complexity: O(1)
661
- *
662
- * The function `splice` in TypeScript overrides the default behavior to remove and insert elements
663
- * in a singly linked list while handling boundary cases.
664
- * @param {number} start - The `start` parameter in the `splice` method indicates the index at which
665
- * to start modifying the list. It specifies the position where elements will be added or removed.
666
- * @param {number} [deleteCount=0] - The `deleteCount` parameter in the `splice` method specifies the
667
- * number of elements to remove from the array starting at the specified `start` index. If
668
- * `deleteCount` is not provided, it defaults to 0, meaning no elements will be removed but new
669
- * elements can still be inserted at
670
- * @param {E[]} items - The `items` parameter in the `splice` method represents the elements to be
671
- * inserted into the list at the specified `start` index. These elements will be inserted in place of
672
- * the elements that are removed from the list. The `splice` method allows you to add new elements to
673
- * the list while
674
- * @returns The `splice` method is returning a `SinglyLinkedList` containing the elements that were
675
- * removed from the original list during the splice operation.
676
- */
677
- override splice(start: number, deleteCount: number = 0, ...items: E[]): this {
678
- const removedList = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen });
679
-
680
- // If `start` is out of range, perform boundary processing
594
+ * Remove and/or insert elements at a position (array-like behavior).
595
+ * @remarks Time O(N + M), Space O(M)
596
+ * @param start - Start index (clamped to [0, length]).
597
+ * @param [deleteCount] - Number of elements to remove (default 0).
598
+ * @param [items] - Elements to insert after `start`.
599
+ * @returns A new list containing the removed elements (typed as `this`).
600
+ */
601
+
602
+ override splice(start: number, deleteCount = 0, ...items: E[]): this {
681
603
  start = Math.max(0, Math.min(start, this.length));
682
604
  deleteCount = Math.max(0, deleteCount);
683
605
 
684
- // Find the predecessor node of `start`
606
+ const removedList = this._createInstance();
607
+
685
608
  const prevNode = start === 0 ? undefined : this.getNodeAt(start - 1);
686
- const startNode = prevNode ? prevNode.next : this.head;
609
+ let cur = prevNode ? prevNode.next : this.head;
687
610
 
688
- let current = startNode;
689
- for (let i = 0; i < deleteCount && current; i++) {
690
- removedList.push(current.value);
691
- current = current.next;
611
+ let removedCount = 0;
612
+ while (removedCount < deleteCount && cur) {
613
+ removedList.push(cur.value);
614
+ cur = cur.next;
615
+ removedCount++;
692
616
  }
617
+ const afterNode = cur;
693
618
 
694
- const nextNode = current;
695
- let lastInsertedNode: SinglyLinkedListNode<E> | undefined = undefined;
696
-
697
- for (const item of items) {
698
- const newNode = this._ensureNode(item);
699
- if (!lastInsertedNode) {
700
- if (prevNode) {
701
- prevNode.next = newNode;
702
- } else {
703
- this._head = newNode;
704
- }
705
- } else {
706
- lastInsertedNode.next = newNode;
619
+ if (prevNode) {
620
+ prevNode.next = afterNode;
621
+ } else {
622
+ this._head = afterNode;
623
+ }
624
+ if (!afterNode) this._tail = prevNode;
625
+
626
+ if (items.length > 0) {
627
+ let firstInserted: SinglyLinkedListNode<E> | undefined;
628
+ let lastInserted: SinglyLinkedListNode<E> | undefined;
629
+ for (const it of items) {
630
+ const node = this._ensureNode(it);
631
+ if (!firstInserted) firstInserted = node;
632
+ if (lastInserted) lastInserted.next = node;
633
+ lastInserted = node;
707
634
  }
708
- lastInsertedNode = newNode;
709
- }
635
+ if (prevNode) prevNode.next = firstInserted!;
636
+ else this._head = firstInserted!;
710
637
 
711
- // Connect new node to `nextNode`
712
- if (lastInsertedNode) {
713
- lastInsertedNode.next = nextNode;
714
- } else if (prevNode) {
715
- prevNode.next = nextNode;
638
+ lastInserted!.next = afterNode;
639
+ if (!afterNode) this._tail = lastInserted!;
716
640
  }
717
641
 
718
- // Update tail node and length
719
- if (!nextNode) {
720
- this._tail = lastInsertedNode || prevNode;
642
+ this._length += items.length - removedCount;
643
+ if (this._length === 0) {
644
+ this._head = undefined;
645
+ this._tail = undefined;
721
646
  }
722
- this._length += items.length - removedList.length;
723
647
 
724
- return removedList as this;
648
+ return removedList as unknown as this;
725
649
  }
726
650
 
727
651
  /**
728
- * Time Complexity: O(n)
729
- * Space Complexity: O(1)
730
- *
731
- * The function `countOccurrences` iterates through a singly linked list and counts the occurrences
732
- * of a specified element or nodes that satisfy a given predicate.
733
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)} elementOrNode
734
- * - The `elementOrNode` parameter in the `countOccurrences` method can accept three types of values:
735
- * @returns The `countOccurrences` method returns the number of occurrences of the specified element,
736
- * node, or predicate function in the singly linked list.
652
+ * Count how many nodes match a value/node/predicate.
653
+ * @remarks Time O(N), Space O(1)
654
+ * @param elementOrNode - Element, node, or node predicate to match.
655
+ * @returns Number of matches in the list.
737
656
  */
657
+
738
658
  countOccurrences(elementOrNode: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)): number {
739
- const predicate = this._ensurePredicate(elementOrNode);
659
+ const predicate = elementOrPredicate(elementOrNode, this._equals);
740
660
  let count = 0;
741
661
  let current = this.head;
742
-
743
662
  while (current) {
744
- if (predicate(current)) {
745
- count++;
746
- }
663
+ if (predicate(current)) count++;
747
664
  current = current.next;
748
665
  }
749
-
750
666
  return count;
751
667
  }
752
668
 
753
669
  /**
754
- * Time Complexity: O(n)
755
- * Space Complexity: O(n)
756
- *
757
- * The `clone` function returns a new instance of the `SinglyLinkedList` class with the same values
758
- * as the original list.
759
- * @returns The `clone()` method is returning a new instance of the `SinglyLinkedList` class, which
760
- * is a clone of the original list.
670
+ * Set the equality comparator used to compare values.
671
+ * @remarks Time O(1), Space O(1)
672
+ * @param equals - Equality predicate (a, b) → boolean.
673
+ * @returns This list.
761
674
  */
762
- clone(): this {
763
- return new SinglyLinkedList<E, R>(this, { toElementFn: this.toElementFn, maxLen: this._maxLen }) as this;
764
- }
765
-
766
- /**
767
- * Time Complexity: O(n)
768
- * Space Complexity: O(n)
769
- *
770
- * The `filter` function creates a new SinglyLinkedList by iterating over the elements of the current
771
- * list and applying a callback function to each element to determine if it should be included in the
772
- * filtered list.
773
- * @param callback - The callback parameter is a function that will be called for each element in the
774
- * list. It takes three arguments: the current element, the index of the current element, and the
775
- * list itself. The callback function should return a boolean value indicating whether the current
776
- * element should be included in the filtered list or not
777
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
778
- * to be used as `this` when executing the `callback` function. If `thisArg` is provided, it will be
779
- * passed as the `this` value to the `callback` function. If `thisArg` is
780
- * @returns The `filter` method is returning a new `SinglyLinkedList` object that contains the
781
- * elements that pass the filter condition specified by the `callback` function.
782
- */
783
- filter(callback: ElementCallback<E, R, boolean>, thisArg?: any): SinglyLinkedList<E, R> {
784
- const filteredList = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen });
785
- let index = 0;
786
- for (const current of this) {
787
- if (callback.call(thisArg, current, index, this)) {
788
- filteredList.push(current);
675
+
676
+ setEquality(equals: (a: E, b: E) => boolean): this {
677
+ this._equals = equals;
678
+ return this;
679
+ }
680
+
681
+ /**
682
+ * Delete the first node whose value matches a predicate.
683
+ * @remarks Time O(N), Space O(1)
684
+ * @param predicate - Predicate (value, index, list) boolean to decide deletion.
685
+ * @returns True if a node was removed.
686
+ */
687
+
688
+ deleteWhere(predicate: (value: E, index: number, list: this) => boolean): boolean {
689
+ let prev: SinglyLinkedListNode<E> | undefined;
690
+ let current = this.head;
691
+ let i = 0;
692
+ while (current) {
693
+ if (predicate(current.value, i++, this)) {
694
+ if (!prev) {
695
+ this._head = current.next;
696
+ if (current === this._tail) this._tail = undefined;
697
+ } else {
698
+ prev.next = current.next;
699
+ if (current === this._tail) this._tail = prev;
700
+ }
701
+ this._length--;
702
+ return true;
789
703
  }
790
- index++;
791
- }
792
- return filteredList;
793
- }
794
-
795
- /**
796
- * Time Complexity: O(n)
797
- * Space Complexity: O(n)
798
- *
799
- * The `map` function takes a callback function and returns a new SinglyLinkedList with the results
800
- * of applying the callback to each element in the original list.
801
- * @param callback - The `callback` parameter is a function that will be called for each element in
802
- * the original list. It takes three arguments: `current` (the current element being processed),
803
- * `index` (the index of the current element), and `this` (the original list). It should return a
804
- * value
805
- * @param [toElementFn] - The `toElementFn` parameter is an optional function that can be used to
806
- * convert the raw element (`RR`) to the desired element type (`T`). It takes the raw element as
807
- * input and returns the converted element. If this parameter is not provided, the raw element will
808
- * be used as is.
809
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to
810
- * specify the value of `this` within the callback function. It is used to set the context or scope
811
- * in which the callback function will be executed. If `thisArg` is provided, it will be used as the
812
- * value of
813
- * @returns a new instance of the `SinglyLinkedList` class with the mapped elements.
814
- */
815
- map<EM, RM>(
816
- callback: ElementCallback<E, R, EM>,
817
- toElementFn?: (rawElement: RM) => EM,
818
- thisArg?: any
819
- ): SinglyLinkedList<EM, RM> {
820
- const mappedList = new SinglyLinkedList<EM, RM>([], { toElementFn, maxLen: this._maxLen });
821
- let index = 0;
822
- for (const current of this) {
823
- mappedList.push(callback.call(thisArg, current, index, this));
824
- index++;
704
+ prev = current;
705
+ current = current.next;
825
706
  }
826
-
827
- return mappedList;
707
+ return false;
828
708
  }
829
709
 
830
710
  /**
831
- * The function `_createInstance` returns a new instance of `SinglyLinkedList` with the specified
832
- * options.
833
- * @param [options] - The `options` parameter in the `_createInstance` method is of type
834
- * `SinglyLinkedListOptions<E, R>`, which is used to configure the behavior of the `SinglyLinkedList`
835
- * instance being created. It is an optional parameter, meaning it can be omitted when calling the
836
- * method.
837
- * @returns An instance of the `SinglyLinkedList` class with an empty array and the provided options
838
- * is being returned.
711
+ * Deep clone this list (values are copied by reference).
712
+ * @remarks Time O(N), Space O(N)
713
+ * @returns A new list with the same element sequence.
839
714
  */
840
- protected override _createInstance(options?: SinglyLinkedListOptions<E, R>): this {
841
- return new SinglyLinkedList<E, R>([], options) as this;
715
+
716
+ clone(): this {
717
+ const out = this._createInstance();
718
+ for (const v of this) out.push(v);
719
+ return out;
842
720
  }
843
721
 
844
722
  /**
845
- * The function `_getIterator` returns an iterable iterator that yields the values of a linked list.
723
+ * Filter values into a new list of the same class.
724
+ * @remarks Time O(N), Space O(N)
725
+ * @param callback - Predicate (value, index, list) → boolean to keep value.
726
+ * @param [thisArg] - Value for `this` inside the callback.
727
+ * @returns A new list with kept values.
846
728
  */
847
- protected *_getIterator(): IterableIterator<E> {
848
- let current = this.head;
849
729
 
850
- while (current) {
851
- yield current.value;
852
- current = current.next;
853
- }
730
+ filter(callback: ElementCallback<E, R, boolean>, thisArg?: any): this {
731
+ const out = this._createInstance();
732
+ let index = 0;
733
+ for (const value of this) if (callback.call(thisArg, value, index++, this)) out.push(value);
734
+ return out;
854
735
  }
855
736
 
856
737
  /**
857
- * The function returns an iterator that iterates over the elements of a collection in reverse order.
738
+ * Map values into a new list of the same class.
739
+ * @remarks Time O(N), Space O(N)
740
+ * @param callback - Mapping function (value, index, list) → newValue.
741
+ * @param [thisArg] - Value for `this` inside the callback.
742
+ * @returns A new list with mapped values.
858
743
  */
859
- protected *_getReverseIterator(): IterableIterator<E> {
860
- const reversedArr = [...this].reverse();
861
744
 
862
- for (const item of reversedArr) {
863
- yield item;
745
+ mapSame(callback: ElementCallback<E, R, E>, thisArg?: any): this {
746
+ const out = this._createInstance();
747
+ let index = 0;
748
+ for (const value of this) {
749
+ const mv = thisArg === undefined ? callback(value, index++, this) : callback.call(thisArg, value, index++, this);
750
+ out.push(mv);
864
751
  }
752
+ return out;
865
753
  }
866
754
 
867
755
  /**
868
- * The function `_getNodeIterator` returns an iterator that iterates over the nodes of a singly
869
- * linked list.
756
+ * Map values into a new list (possibly different element type).
757
+ * @remarks Time O(N), Space O(N)
758
+ * @template EM
759
+ * @template RM
760
+ * @param callback - Mapping function (value, index, list) → newElement.
761
+ * @param [options] - Options for the output list (e.g., maxLen, toElementFn).
762
+ * @param [thisArg] - Value for `this` inside the callback.
763
+ * @returns A new SinglyLinkedList with mapped values.
870
764
  */
871
- protected *_getNodeIterator(): IterableIterator<SinglyLinkedListNode<E>> {
872
- let current = this.head;
873
765
 
874
- while (current) {
875
- yield current;
876
- current = current.next;
877
- }
766
+ map<EM, RM = any>(
767
+ callback: ElementCallback<E, R, EM>,
768
+ options?: SinglyLinkedListOptions<EM, RM>,
769
+ thisArg?: any
770
+ ): SinglyLinkedList<EM, RM> {
771
+ const out = this._createLike<EM, RM>([], { ...(options ?? {}), maxLen: this._maxLen as number });
772
+ let index = 0;
773
+ for (const value of this) out.push(callback.call(thisArg, value, index++, this));
774
+ return out;
878
775
  }
879
776
 
880
- // protected *_getReverseNodeIterator(): IterableIterator<SinglyLinkedListNode<E>> {
881
- // const reversedArr = [...this._getNodeIterator()].reverse();
882
- //
883
- // for (const item of reversedArr) {
884
- // yield item;
885
- // }
886
- // }
777
+ /**
778
+ * (Protected) Create a node from a value.
779
+ * @remarks Time O(1), Space O(1)
780
+ * @param value - Value to wrap in a node.
781
+ * @returns A new SinglyLinkedListNode instance.
782
+ */
783
+
784
+ protected _createNode(value: E): SinglyLinkedListNode<E> {
785
+ return new SinglyLinkedListNode<E>(value);
786
+ }
887
787
 
888
788
  /**
889
- * The _isPredicate function in TypeScript checks if the input is a function that takes a
890
- * SinglyLinkedListNode as an argument and returns a boolean.
891
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)} elementNodeOrPredicate
892
- * elementNodeOrPredicate - The `elementNodeOrPredicate` parameter can be one of the following types:
893
- * @returns The _isPredicate method is returning a boolean value based on whether the
894
- * elementNodeOrPredicate parameter is a function or not. If the elementNodeOrPredicate is a
895
- * function, the method will return true, indicating that it is a predicate function. If it is not a
896
- * function, the method will return false.
789
+ * (Protected) Check if input is a node predicate function.
790
+ * @remarks Time O(1), Space O(1)
791
+ * @param elementNodeOrPredicate - Element, node, or node predicate.
792
+ * @returns True if input is a predicate function.
897
793
  */
794
+
898
795
  protected _isPredicate(
899
796
  elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)
900
797
  ): elementNodeOrPredicate is (node: SinglyLinkedListNode<E>) => boolean {
@@ -902,55 +799,136 @@ export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, S
902
799
  }
903
800
 
904
801
  /**
905
- * The function `_ensureNode` ensures that the input is a valid node and returns it, creating a new
906
- * node if necessary.
907
- * @param {E | SinglyLinkedListNode<E>} elementOrNode - The `elementOrNode` parameter can be either
908
- * an element of type `E` or a `SinglyLinkedListNode` containing an element of type `E`.
909
- * @returns A SinglyLinkedListNode<E> object is being returned.
802
+ * (Protected) Normalize input into a node instance.
803
+ * @remarks Time O(1), Space O(1)
804
+ * @param elementOrNode - Element or node.
805
+ * @returns A SinglyLinkedListNode for the provided input.
910
806
  */
807
+
911
808
  protected _ensureNode(elementOrNode: E | SinglyLinkedListNode<E>) {
912
809
  if (this.isNode(elementOrNode)) return elementOrNode;
913
-
914
- return new SinglyLinkedListNode<E>(elementOrNode);
810
+ return this._createNode(elementOrNode);
915
811
  }
916
812
 
917
813
  /**
918
- * The function `_ensurePredicate` in TypeScript ensures that the input is either a node, a predicate
919
- * function, or a value to compare with the node's value.
920
- * @param {E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)} elementNodeOrPredicate
921
- * elementNodeOrPredicate - The `elementNodeOrPredicate` parameter can be one of the following types:
922
- * @returns A function is being returned. If the input `elementNodeOrPredicate` is already a node, a
923
- * function is returned that checks if a given node is equal to the input node. If the input is a
924
- * predicate function, it is returned as is. If the input is neither a node nor a predicate function,
925
- * a function is returned that checks if a given node's value is equal to the input
814
+ * (Protected) Normalize input into a node predicate.
815
+ * @remarks Time O(1), Space O(1)
816
+ * @param elementNodeOrPredicate - Element, node, or predicate.
817
+ * @returns A predicate taking a node and returning true/false.
926
818
  */
819
+
927
820
  protected _ensurePredicate(
928
821
  elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)
929
822
  ) {
930
823
  if (this.isNode(elementNodeOrPredicate)) return (node: SinglyLinkedListNode<E>) => node === elementNodeOrPredicate;
931
-
932
824
  if (this._isPredicate(elementNodeOrPredicate)) return elementNodeOrPredicate;
933
-
934
- return (node: SinglyLinkedListNode<E>) => node.value === elementNodeOrPredicate;
825
+ const value = elementNodeOrPredicate as E;
826
+ return (node: SinglyLinkedListNode<E>) => this._equals(node.value, value);
935
827
  }
936
828
 
937
829
  /**
938
- * The function `_getPrevNode` returns the node before a given node in a singly linked list.
939
- * @param node - The `node` parameter in the `_getPrevNode` method is a reference to a node in a
940
- * singly linked list. The method is used to find the node that comes before the given node in the
941
- * linked list.
942
- * @returns The `_getPrevNode` method returns either the previous node of the input node in a singly
943
- * linked list or `undefined` if the input node is the head of the list or if the input node is not
944
- * found in the list.
830
+ * (Protected) Get the previous node of a given node.
831
+ * @remarks Time O(N), Space O(1)
832
+ * @param node - A node in the list.
833
+ * @returns Previous node or undefined.
945
834
  */
835
+
946
836
  protected _getPrevNode(node: SinglyLinkedListNode<E>): SinglyLinkedListNode<E> | undefined {
947
837
  if (!this.head || this.head === node) return undefined;
838
+ let current = this.head;
839
+ while (current.next && current.next !== node) current = current.next;
840
+ return current.next === node ? current : undefined;
841
+ }
842
+
843
+ /**
844
+ * (Protected) Iterate values from head to tail.
845
+ * @remarks Time O(N), Space O(1)
846
+ * @returns Iterator of values (E).
847
+ */
948
848
 
849
+ protected *_getIterator(): IterableIterator<E> {
949
850
  let current = this.head;
950
- while (current.next && current.next !== node) {
851
+ while (current) {
852
+ yield current.value;
951
853
  current = current.next;
952
854
  }
855
+ }
953
856
 
954
- return current.next === node ? current : undefined;
857
+ /**
858
+ * (Protected) Iterate values from tail to head.
859
+ * @remarks Time O(N), Space O(N)
860
+ * @returns Iterator of values (E).
861
+ */
862
+
863
+ protected *_getReverseIterator(): IterableIterator<E> {
864
+ const reversedArr = [...this].reverse();
865
+ for (const item of reversedArr) yield item;
955
866
  }
867
+
868
+ /**
869
+ * (Protected) Iterate nodes from head to tail.
870
+ * @remarks Time O(N), Space O(1)
871
+ * @returns Iterator of nodes.
872
+ */
873
+
874
+ protected *_getNodeIterator(): IterableIterator<SinglyLinkedListNode<E>> {
875
+ let current = this.head;
876
+ while (current) {
877
+ yield current;
878
+ current = current.next;
879
+ }
880
+ }
881
+
882
+ /**
883
+ * (Protected) Create an empty instance of the same concrete class.
884
+ * @remarks Time O(1), Space O(1)
885
+ * @param [options] - Options forwarded to the constructor.
886
+ * @returns An empty like-kind list instance.
887
+ */
888
+
889
+ protected _createInstance(options?: SinglyLinkedListOptions<E, R>): this {
890
+ const Ctor: any = this.constructor;
891
+ return new Ctor([], options);
892
+ }
893
+
894
+ /**
895
+ * (Protected) Create a like-kind instance and seed it from an iterable.
896
+ * @remarks Time O(N), Space O(N)
897
+ * @template EM
898
+ * @template RM
899
+ * @param [elements] - Iterable used to seed the new list.
900
+ * @param [options] - Options forwarded to the constructor.
901
+ * @returns A like-kind SinglyLinkedList instance.
902
+ */
903
+
904
+ protected _createLike<EM, RM>(
905
+ elements: Iterable<EM> | Iterable<RM> | Iterable<SinglyLinkedListNode<EM>> = [],
906
+ options?: SinglyLinkedListOptions<EM, RM>
907
+ ): SinglyLinkedList<EM, RM> {
908
+ const Ctor: any = this.constructor;
909
+ return new Ctor(elements, options) as SinglyLinkedList<EM, RM>;
910
+ }
911
+
912
+ /**
913
+ * (Protected) Spawn an empty like-kind list instance.
914
+ * @remarks Time O(1), Space O(1)
915
+ * @template EM
916
+ * @template RM
917
+ * @param [options] - Options forwarded to the constructor.
918
+ * @returns An empty like-kind SinglyLinkedList instance.
919
+ */
920
+
921
+ protected _spawnLike<EM, RM>(options?: SinglyLinkedListOptions<EM, RM>): SinglyLinkedList<EM, RM> {
922
+ return this._createLike<EM, RM>([], options);
923
+ }
924
+ }
925
+
926
+ function elementOrPredicate<E>(
927
+ input: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean),
928
+ equals: (a: E, b: E) => boolean
929
+ ) {
930
+ if (input instanceof SinglyLinkedListNode) return (node: SinglyLinkedListNode<E>) => node === input;
931
+ if (typeof input === 'function') return input as (node: SinglyLinkedListNode<E>) => boolean;
932
+ const value = input as E;
933
+ return (node: SinglyLinkedListNode<E>) => equals(node.value, value);
956
934
  }