dlinked 0.1.8 → 0.1.9

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.
data/lib/d_linked/list.rb CHANGED
@@ -3,16 +3,34 @@
3
3
  require_relative 'list/node'
4
4
 
5
5
  module DLinked
6
- # A fast, lightweight doubly linked list implementation
6
+ # A fast, lightweight, and standards-compliant doubly linked list.
7
+ #
8
+ # This class provides a memory-efficient and performant alternative to Ruby's
9
+ # standard `Array` for scenarios requiring frequent O(1) insertions or
10
+ # deletions from the ends of the list (e.g., queues, stacks).
11
+ #
12
+ # It includes the `Enumerable` module, allowing it to work seamlessly with
13
+ # standard Ruby collection methods like `map`, `select`, and `each`.
14
+ #
15
+ # @example Basic Usage
16
+ # list = DLinked::List.new
17
+ # list.append(20).prepend(10) # => [10, 20]
18
+ # list.shift # => 10
19
+ # list.to_a # => [20]
7
20
  class List
8
21
  include Enumerable
9
22
 
10
- # PURE PERFORMANCE: We now use the dedicated Class for Node,
11
- # Node = DLinked::List::Node
12
-
23
+ # @!attribute [r] size
24
+ # The number of elements in the list.
25
+ # @return [Integer] The count of elements.
13
26
  attr_reader :size
14
27
  alias length size
15
28
 
29
+ # Initializes a new, empty list.
30
+ #
31
+ # @example
32
+ # list = DLinked::List.new
33
+ # list.size # => 0
16
34
  def initialize
17
35
  @head = nil
18
36
  @tail = nil
@@ -23,22 +41,34 @@ module DLinked
23
41
 
24
42
  # Adds a new value to the beginning of the list (the head).
25
43
  #
26
- # This is an **O(1)** operation, as it only involves updating a few pointers.
44
+ # This is an **O(1)** operation.
45
+ #
46
+ # @example
47
+ # list = DLinked::List.new
48
+ # list.prepend(10)
49
+ # list.prepend(5)
50
+ # list.to_a # => [5, 10]
27
51
  #
28
52
  # @param value [Object] The value to store in the new node.
29
- # @return [self] Returns the list instance, allowing for method chaining (e.g., list.prepend(1).prepend(2)).
53
+ # @return [self] The list instance, allowing for method chaining.
30
54
  def prepend(value)
31
55
  node = Node.new(value, nil, @head)
32
56
  list_prepend_logic(node)
33
57
  end
34
58
  alias unshift prepend
35
-
59
+
36
60
  # Adds a new value to the end of the list (the tail).
37
61
  #
38
- # This is an O(1) operation.
62
+ # This is an **O(1)** operation.
63
+ #
64
+ # @example
65
+ # list = DLinked::List.new
66
+ # list.append(10)
67
+ # list.append(20)
68
+ # list.to_a # => [10, 20]
39
69
  #
40
70
  # @param value [Object] The value to add to the list.
41
- # @return [DLinked::List] Returns the list instance for method chaining.
71
+ # @return [self] The list instance, for method chaining.
42
72
  def append(value)
43
73
  node = Node.new(value, @tail, nil)
44
74
  list_append_logic(node)
@@ -50,7 +80,13 @@ module DLinked
50
80
  #
51
81
  # This is an **O(1)** operation.
52
82
  #
53
- # @return [Object, nil] The value of the removed element, or nil if the list is empty.
83
+ # @example
84
+ # list = DLinked::List.new << 1 << 2
85
+ # list.shift # => 1
86
+ # list.shift # => 2
87
+ # list.shift # => nil
88
+ #
89
+ # @return [Object, nil] The value of the removed element, or `nil` if the list is empty.
54
90
  def shift
55
91
  return nil if empty?
56
92
 
@@ -63,9 +99,15 @@ module DLinked
63
99
 
64
100
  # Removes the last element from the list (the tail) and returns its value.
65
101
  #
66
- # This is an **O(1)** operation, as the tail pointer gives immediate access to the node.
102
+ # This is an **O(1)** operation.
103
+ #
104
+ # @example
105
+ # list = DLinked::List.new << 1 << 2
106
+ # list.pop # => 2
107
+ # list.pop # => 1
108
+ # list.pop # => nil
67
109
  #
68
- # @return [Object, nil] The value of the removed element, or nil if the list is empty.
110
+ # @return [Object, nil] The value of the removed element, or `nil` if the list is empty.
69
111
  def pop
70
112
  return nil if empty?
71
113
 
@@ -76,20 +118,20 @@ module DLinked
76
118
  value
77
119
  end
78
120
 
79
- # Returns the value of the element at the head (start) of the list.
121
+ # Returns the value of the element at the head of the list without removing it.
80
122
  #
81
123
  # This is an **O(1)** operation.
82
124
  #
83
- # @return [Object, nil] The value of the first element, or nil if the list is empty.
125
+ # @return [Object, nil] The value of the first element, or `nil` if the list is empty.
84
126
  def first
85
127
  @head&.value
86
128
  end
87
129
 
88
- # Returns the value of the element at the tail (end) of the list.
130
+ # Returns the value of the element at the tail of the list without removing it.
89
131
  #
90
132
  # This is an **O(1)** operation.
91
133
  #
92
- # @return [Object, nil] The value of the last element, or nil if the list is empty.
134
+ # @return [Object, nil] The value of the last element, or `nil` if the list is empty.
93
135
  def last
94
136
  @tail&.value
95
137
  end
@@ -98,16 +140,16 @@ module DLinked
98
140
  #
99
141
  # This is an **O(1)** operation.
100
142
  #
101
- # @return [Boolean] True if the size of the list is zero, false otherwise.
143
+ # @return [Boolean] `true` if the list is empty, `false` otherwise.
102
144
  def empty?
103
145
  @size.zero?
104
146
  end
105
147
 
106
- # Removes all elements from the list, resetting the head, tail, and size.
148
+ # Removes all elements from the list.
107
149
  #
108
- # This is an **O(1)** operation, as it only resets instance variables.
150
+ # This is an **O(1)** operation.
109
151
  #
110
- # @return [self] Returns the list instance.
152
+ # @return [self] The cleared list instance.
111
153
  def clear
112
154
  @head = nil
113
155
  @tail = nil
@@ -117,13 +159,12 @@ module DLinked
117
159
 
118
160
  # --- O(n) ENUMERATION & LOOKUP ---
119
161
 
120
- # Iterates through the list, yielding the value of each element in order.
162
+ # Iterates through the list, yielding the value of each element in order from head to tail.
121
163
  #
122
- # This is an **O(n)** operation, as it traverses every node from head to tail.
164
+ # This is an **O(n)** operation.
123
165
  #
124
166
  # @yield [Object] The value of the current element.
125
- # @return [self, Enumerator] Returns the list instance if a block is given,
126
- # otherwise returns an Enumerator.
167
+ # @return [self, Enumerator] Returns `self` if a block is given, otherwise returns an `Enumerator`.
127
168
  def each
128
169
  return enum_for(:each) unless block_given?
129
170
 
@@ -135,14 +176,12 @@ module DLinked
135
176
  self
136
177
  end
137
178
 
138
- # Iterates through the list in reverse order, yielding the value of each element
139
- # starting from the tail and moving to the head.
179
+ # Iterates through the list in reverse order, from tail to head.
140
180
  #
141
- # This is an **O(n)** operation, as it traverses every node.
181
+ # This is an **O(n)** operation.
142
182
  #
143
183
  # @yield [Object] The value of the current element.
144
- # @return [self, Enumerator] Returns the list instance if a block is given,
145
- # otherwise returns an Enumerator.
184
+ # @return [self, Enumerator] Returns `self` if a block is given, otherwise returns an `Enumerator`.
146
185
  def reverse_each
147
186
  return enum_for(:reverse_each) unless block_given?
148
187
 
@@ -156,10 +195,10 @@ module DLinked
156
195
 
157
196
  # Finds the index of the first occurrence of a given value.
158
197
  #
159
- # This is an **O(n)** operation, as it requires traversing the list from the head.
198
+ # This is an **O(n)** operation.
160
199
  #
161
200
  # @param value [Object] The value to search for.
162
- # @return [Integer, nil] The index of the first matching element, or nil if the value is not found.
201
+ # @return [Integer, nil] The index of the first matching element, or `nil` if not found.
163
202
  def index(value)
164
203
  current = @head
165
204
  idx = 0
@@ -174,19 +213,18 @@ module DLinked
174
213
 
175
214
  # Converts the linked list into a standard Ruby Array.
176
215
  #
177
- # This is an **O(n)** operation, as it requires iterating over every element
178
- # and allocating a new Array.
216
+ # This is an **O(n)** operation.
179
217
  #
180
- # @return [Array] A new Array containing all elements in order.
218
+ # @return [Array] A new `Array` containing all elements in order.
181
219
  def to_a
182
220
  map { |v| v }
183
221
  end
184
222
 
185
- # Returns a string representation of the list, resembling a standard Ruby Array.
223
+ # Returns a string representation of the list.
186
224
  #
187
- # This is an **O(n)** operation due to the call to #to_a.
225
+ # This is an **O(n)** operation.
188
226
  #
189
- # @return [String] The string representation (e.g., "[10, 20, 30]").
227
+ # @return [String] The string representation (e.g., `"[10, 20, 30]"`).
190
228
  def to_s
191
229
  "[#{to_a.join(', ')}]"
192
230
  end
@@ -194,20 +232,22 @@ module DLinked
194
232
 
195
233
  # --- O(n) ARRAY/SLICE COMPATIBILITY ---
196
234
 
197
- # Retrieves the element(s) at the specified index or within a slice.
198
- #
199
- # This method supports two primary forms of access:
200
- # 1. **Single Index (O(n)):** Returns the element at a specific positive or negative index (e.g., list[2] or list[-1]).
201
- # 2. **Slice Access (O(n)):** Delegates to the {#slice} method for start/length or range access (e.g., list[1, 2] or list[1..3]).
202
- #
203
- # Traversal is optimized: for positive indices less than size/2, traversal starts from the head;
204
- # otherwise, it starts from the tail.
205
- #
206
- # @param args [Array] Arguments representing either a single index or slice parameters:
207
- # - `(index)` for single element access.
208
- # - `(start_index, length)` for a slice.
209
- # - `(range)` for a slice using a Range object.
210
- # @return [Object, Array<Object>, nil] The value at the index, an array of values for a slice, or nil if the single index is out of bounds.
235
+ # @overload [](index)
236
+ # Retrieves the element at a specific index.
237
+ # Traversal is optimized to start from the head or tail, whichever is closer.
238
+ # @param index [Integer] The index (positive or negative).
239
+ # @return [Object, nil] The value at the index, or `nil` if out of bounds.
240
+ #
241
+ # @overload [](start, length)
242
+ # Retrieves a slice of `length` elements starting at `start`.
243
+ # @param start [Integer] The starting index.
244
+ # @param length [Integer] The number of elements to retrieve.
245
+ # @return [DLinked::List, nil] A new list containing the slice, or `nil` if `start` is out of bounds.
246
+ #
247
+ # @overload [](range)
248
+ # Retrieves a slice using a `Range`.
249
+ # @param range [Range] The range of indices to retrieve.
250
+ # @return [DLinked::List] A new list containing the slice.
211
251
  def [](*args)
212
252
  # Case 1: Single Index Access (list[i])
213
253
  if args.size == 1 && args[0].is_a?(Integer)
@@ -223,27 +263,24 @@ module DLinked
223
263
  slice(*args) # Delegate to the robust slice method
224
264
  end
225
265
 
226
- # Sets the value of an element at a single index or replaces a slice (range or start/length)
227
- # with new element(s).
228
- #
229
- # This method handles four main scenarios:
230
- # 1. Single Element Assignment (O(n)): Overwrites the value at a valid index.
231
- # 2. Slice Replacement (O(n)): Deletes a section and inserts new elements.
232
- # 3. Out-of-Bounds Append (O(k)): If the start index is greater than the current size,
233
- # the new elements are appended to the list (k is the length of the replacement).
234
- # 4. Out-of-Bounds Non-Append (Returns nil): For a single index assignment that is out of bounds,
235
- # it returns nil (like a standard Ruby Array setter).
236
- #
237
- # The overall complexity is **O(n + k)**, where n is the traversal time to find the start point,
238
- # and k is the number of elements being inserted or deleted.
239
- #
240
- # @param args [Array] The arguments defining the assignment. The last element of this array
241
- # is always the replacement value.
242
- # - `(index, replacement)` for single assignment.
243
- # - `(start_index, length, replacement)` for slice replacement.
244
- # - `(range, replacement)` for range replacement.
245
- # @return [Object, Array, nil] The value(s) assigned, or nil if the assignment failed
246
- # due to an invalid out-of-bounds single index.
266
+ # @overload []=(index, value)
267
+ # Sets the value of an element at a single index.
268
+ # @param index [Integer] The index to modify.
269
+ # @param value [Object] The new value.
270
+ # @return [Object, nil] The assigned value, or `nil` if the index is out of bounds.
271
+ #
272
+ # @overload []=(start, length, value)
273
+ # Replaces a slice of `length` elements starting at `start` with a new value (or values).
274
+ # @param start [Integer] The starting index.
275
+ # @param length [Integer] The number of elements to replace.
276
+ # @param value [Object, Array<Object>] The new value(s).
277
+ # @return [Object, Array<Object>] The assigned value(s).
278
+ #
279
+ # @overload []=(range, value)
280
+ # Replaces a slice defined by a `Range` with a new value (or values).
281
+ # @param range [Range] The range of indices to replace.
282
+ # @param value [Object, Array<Object>] The new value(s).
283
+ # @return [Object, Array<Object>] The assigned value(s).
247
284
  def []=(*args)
248
285
  replacement = args.pop
249
286
 
@@ -251,20 +288,14 @@ module DLinked
251
288
  if args.size == 1 && args[0].is_a?(Integer)
252
289
  index = args[0]
253
290
  index += @size if index.negative?
254
- # Check bounds for simple assignment (Must be within 0 to size-1)
255
291
  return nil unless index >= 0 && index < @size
256
292
 
257
- # Simple assignment: O(n) lookup, O(1) set
258
293
  node = find_node_at_index(index)
259
294
  node.value = replacement
260
295
  return replacement
261
-
262
- # For out-of-bounds, Array compatibility is usually IndexError, but
263
- # based on your design, we return nil
264
-
265
296
  end
266
297
 
267
- # 2. Handle Slice Replacement (list[2, 3] = [a, b] or list[2..4] = [a, b])
298
+ # 2. Handle Slice Replacement
268
299
  start_index, length = *args
269
300
 
270
301
  if args.size == 1 && start_index.is_a?(Range)
@@ -285,20 +316,17 @@ module DLinked
285
316
 
286
317
  replacement = Array(replacement)
287
318
 
288
- # Find Boundaries
289
319
  predecessor = start_index.positive? ? find_node_at_index(start_index - 1) : nil
290
320
  current = predecessor ? predecessor.next : @head
291
321
 
292
322
  deleted_count = 0
293
323
  length.times do
294
324
  break unless current
295
-
296
325
  current = current.next
297
326
  deleted_count += 1
298
327
  end
299
328
  successor = current
300
329
 
301
- # Stage 1: DELETION (Relink the neighbors)
302
330
  if predecessor
303
331
  predecessor.next = successor
304
332
  else
@@ -312,43 +340,37 @@ module DLinked
312
340
  end
313
341
  @size -= deleted_count
314
342
 
315
- # Stage 2: INSERTION (Insert new nodes at the boundary)
316
343
  insertion_point = predecessor
317
344
  replacement.each do |value|
318
345
  new_node = Node.new(value, insertion_point, successor)
319
-
320
346
  if insertion_point
321
347
  insertion_point.next = new_node
322
348
  else
323
349
  @head = new_node
324
350
  end
325
-
326
351
  insertion_point = new_node
327
352
  @size += 1
328
353
  end
329
354
 
330
- # Stage 3: FINAL RELINKING (The last inserted node links back to the successor)
331
355
  if successor
332
356
  successor.prev = insertion_point
333
357
  else
334
358
  @tail = insertion_point
335
359
  end
336
360
 
337
- replacement # Return the set values
361
+ replacement
338
362
  end
339
363
 
340
364
  # Inserts a new element at the specified index.
341
365
  #
342
- # If the index is 0, this is equivalent to {#prepend} (O(1)).
343
- # If the index is equal to or greater than the size, this is equivalent to {#append} (O(1)).
344
- # For all other valid indices, this is an **O(n)** operation as it requires traversal
345
- # to find the insertion point.
346
- #
347
- # Supports negative indices, where list.insert(-1, value) inserts before the last element.
366
+ # This is an **O(n)** operation, unless inserting at the head (`index` = 0)
367
+ # or tail (`index` >= `size`), in which case it is **O(1)**.
348
368
  #
349
- # @param index [Integer] The index before which the new element should be inserted.
350
- # @param value [Object] The value to be inserted.
351
- # @return [DLinked::List] Returns the list instance for method chaining.
369
+ # @param index [Integer] The index before which to insert the new element.
370
+ # @param value [Object] The value to insert.
371
+ # @return [self] The list instance for method chaining.
372
+ # @see #prepend
373
+ # @see #append
352
374
  def insert(index, value)
353
375
  if index.negative?
354
376
  index += @size
@@ -358,12 +380,8 @@ module DLinked
358
380
  return prepend(value) if index <= 0
359
381
  return append(value) if index >= @size
360
382
 
361
- # Find the node to insert BEFORE
362
383
  current = find_node_at_index(index)
363
-
364
384
  new_node = Node.new(value, current.prev, current)
365
-
366
- # Insert before current node (O(1) linking)
367
385
  current.prev.next = new_node
368
386
  current.prev = new_node
369
387
 
@@ -371,13 +389,12 @@ module DLinked
371
389
  self
372
390
  end
373
391
 
374
- # Deletes the *first* node that matches the given value and returns the value of the deleted element.
392
+ # Deletes the *first* node that matches the given value.
375
393
  #
376
- # This is an **O(n)** operation because it requires traversal to find the node.
377
- # However, once the node is found, the relinking operation is O(1).
394
+ # This is an **O(n)** operation.
378
395
  #
379
396
  # @param value [Object] The value to search for and delete.
380
- # @return [Object, nil] The value of the deleted element, or nil if the value was not found in the list.
397
+ # @return [Object, nil] The value of the deleted element, or `nil` if not found.
381
398
  def delete(value)
382
399
  current = @head
383
400
  while current
@@ -390,13 +407,13 @@ module DLinked
390
407
  nil
391
408
  end
392
409
 
393
- # Concatenates the elements of another DLinked::List to the end of this list, modifying the current list.
410
+ # Appends the elements of another list to this one (destructive).
394
411
  #
395
- # This is an **O(n)** operation, where n is the size of the *other* list, as it must traverse and link
396
- # every element of the other list into the current list structure.
412
+ # This is an **O(k)** operation, where `k` is the size of the `other` list.
397
413
  #
398
- # @param other [DLinked::List] The list whose elements will be appended.
399
- # @return [self] Returns the modified list instance.
414
+ # @param other [#each] An enumerable object to append.
415
+ # @return [self] The modified list instance.
416
+ # @raise [TypeError] if `other` is not enumerable.
400
417
  def concat(other)
401
418
  raise TypeError, "can't convert #{other.class} into DLinked::List" unless other.respond_to?(:each)
402
419
  return self if other.empty?
@@ -405,14 +422,13 @@ module DLinked
405
422
  self
406
423
  end
407
424
 
408
- # Returns a new DLinked::List that is the concatenation of this list and another list.
425
+ # Returns a new list by concatenating this list with another (non-destructive).
409
426
  #
410
- # This is a non-destructive operation, meaning neither the current list nor the other list is modified.
411
- # The complexity is **O(n + k)**, where n is the size of the current list and k is the size of the other list,
412
- # as both must be traversed and copied into the new list.
427
+ # The complexity is **O(n + k)**, where `n` is the size of this list and `k`
428
+ # is the size of the `other` list.
413
429
  #
414
- # @param other [DLinked::List] The list to append to this one.
415
- # @return [DLinked::List] A new list containing all elements from both lists.
430
+ # @param other [#each] The enumerable object to concatenate.
431
+ # @return [DLinked::List] A new list containing all elements from both.
416
432
  def +(other)
417
433
  new_list = self.class.new
418
434
  each { |value| new_list.append(value) }
@@ -420,41 +436,37 @@ module DLinked
420
436
  new_list
421
437
  end
422
438
 
423
- # Extracts a slice of elements from the list, returning a new DLinked::List instance.
424
- #
425
- # Supports slicing via:
426
- # 1. Start index and length (e.g., list.slice(1, 2))
427
- # 2. Range (e.g., list.slice(1..3))
439
+ # @overload slice(index)
440
+ # Extracts a single element at `index` and returns it in a new list.
441
+ # @param index [Integer] The index to retrieve.
442
+ # @return [DLinked::List, nil] A new list with one element, or `nil` if out of bounds.
428
443
  #
429
- # This is an **O(n)** operation, where n is the traversal time to find the start point,
430
- # plus the time to copy the slice elements into a new list.
444
+ # @overload slice(start, length)
445
+ # @param start [Integer] The starting index.
446
+ # @param length [Integer] The number of elements in the slice.
447
+ # @return [DLinked::List, nil] A new list, or `nil` if `start` is out of bounds.
431
448
  #
432
- # @param start [Integer, Range] The starting index or a Range object defining the slice.
433
- # @param length [Integer, nil] The number of elements to include in the slice.
434
- # @return [DLinked::List, nil] A new list containing the sliced elements, or nil if the slice is out of bounds.
449
+ # @overload slice(range)
450
+ # @param range [Range] A range of indices.
451
+ # @return [DLinked::List] A new list.
435
452
  def slice(start, length = nil)
436
- # Handle Range Argument
437
453
  if start.is_a?(Range) && length.nil?
438
454
  range = start
439
455
  start = range.begin
440
456
  length = range.end - range.begin + (range.exclude_end? ? 0 : 1)
441
457
  end
442
458
 
443
- # 1. Resolve start index (including negative indices)
444
459
  start += @size if start.negative?
445
-
446
460
  return nil if start.negative? || start >= @size
447
461
 
448
462
  if length.nil?
449
463
  node = find_node_at_index(start)
450
464
  new_list = self.class.new
451
465
  new_list.append(node.value)
452
- return new_list # Returns DLinked::List: [value]
466
+ return new_list
453
467
  end
454
468
 
455
- # Handle negative length returning nil
456
- return nil if length.negative?
457
- return List.new if length.zero?
469
+ return List.new if length.negative?
458
470
 
459
471
  new_list = List.new
460
472
  current = find_node_at_index(start)
@@ -469,19 +481,18 @@ module DLinked
469
481
  new_list
470
482
  end
471
483
 
472
- # Extracts and removes a slice of elements from the list, returning a new list
473
- # containing the removed elements.
484
+ # Extracts and removes a slice of elements, returning the removed slice as a new list.
474
485
  #
475
- # Supports destructive slicing via:
476
- # 1. Start index and length (e.g., list.slice!(1, 2))
477
- # 2. Range (e.g., list.slice!(1..3))
486
+ # This is an **O(n)** operation.
478
487
  #
479
- # The complexity is **O(n + k)**, where n is the traversal time to find the start point,
480
- # and k is the number of elements removed/copied.
488
+ # @overload slice!(start, length)
489
+ # @param start [Integer] The starting index.
490
+ # @param length [Integer] The number of elements to remove.
491
+ # @return [DLinked::List, nil] A new list with the removed elements, or `nil` if the slice is empty.
481
492
  #
482
- # @param args [Array] Arguments representing the slice: (start_index, length) or (range).
483
- # @return [DLinked::List, nil] A new list containing the extracted and removed elements,
484
- # or nil if the slice is empty or invalid.
493
+ # @overload slice!(range)
494
+ # @param range [Range] The range of indices to remove.
495
+ # @return [DLinked::List, nil] A new list with the removed elements, or `nil` if the slice is empty.
485
496
  def slice!(*args)
486
497
  start_index, length = *args
487
498
 
@@ -504,7 +515,6 @@ module DLinked
504
515
 
505
516
  length.times do
506
517
  break unless current
507
-
508
518
  current = current.next
509
519
  end
510
520
  successor = current
@@ -537,9 +547,12 @@ module DLinked
537
547
  @size -= removed_count
538
548
  result.empty? ? nil : result
539
549
  end
550
+
540
551
  protected
541
552
 
542
- # This method handles the actual pointer manipulation, which is constant across all subclasses
553
+ # Handles the O(1) pointer logic for prepending a node.
554
+ # @param node [DLinked::List::Node] The node to prepend.
555
+ # @return [self]
543
556
  def list_prepend_logic(node)
544
557
  @head.prev = node if @head
545
558
  @head = node
@@ -548,6 +561,9 @@ module DLinked
548
561
  self
549
562
  end
550
563
 
564
+ # Handles the O(1) pointer logic for appending a node.
565
+ # @param node [DLinked::List::Node] The node to append.
566
+ # @return [self]
551
567
  def list_append_logic(node)
552
568
  @tail.next = node if @tail
553
569
  @tail = node
@@ -558,7 +574,10 @@ module DLinked
558
574
 
559
575
  private
560
576
 
561
- # O(n/2) - Internal helper method to find the node at a valid index.
577
+ # Finds the node at a valid index using an optimized O(n/2) search.
578
+ # @param index [Integer] The index to find.
579
+ # @return [DLinked::List::Node] The node at the specified index.
580
+ # @api private
562
581
  def find_node_at_index(index)
563
582
  # Optimization: Start from head or tail, whichever is closer
564
583
  if index <= @size / 2
@@ -571,7 +590,9 @@ module DLinked
571
590
  current
572
591
  end
573
592
 
574
- # O(1) - Internal method to delete a specific node
593
+ # Deletes a node from the list in O(1) time.
594
+ # @param node [DLinked::List::Node] The node to delete.
595
+ # @api private
575
596
  def delete_node(node)
576
597
  if node.prev
577
598
  node.prev.next = node.next
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DLinked
4
- VERSION = '0.1.8'
4
+ VERSION = '0.1.9'
5
5
  end