rbtree-ruby 0.1.6 → 0.1.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf6eb0a695ef93f89f9af8ceea5928c549785ea0110992f061b00ecdc76e3d64
4
- data.tar.gz: 458cbee1ded3e8e0686ec495392bffbaf881ac0bc798d68790240abb0a3011b7
3
+ metadata.gz: 8aa1d883925f6b7f608108f1f916598aa04be013aee415c06dfc7bda35d6986c
4
+ data.tar.gz: e935f213100c933b42a3cf0d709809d5ca8d536db56d6e27a6b3de9681847ad9
5
5
  SHA512:
6
- metadata.gz: 48ee9755aad89802b6cb1b6af19a7919151e14531f2c6e2f2dfbb07062c5e1060490e8d5244d0fd960f44d4eb8b00e84ee9f24e5b3be6319c808f212dde848c2
7
- data.tar.gz: aeadf12c689069742c8cc05c569a84bbb6a87b3736f2f7e6351b68fb2aae5dbc24eca80e65a97455b50dae82b47c3ade7b8b70a171d86f879a179865eab9182d
6
+ metadata.gz: 419f0270137b6a6ae39bd73d5ea0c4cc25ff0769c288619ee795d172e724040c6032c09c952745b9bb871ff082861c00ae879fa6a98a571414bad87c2534cb9a
7
+ data.tar.gz: e403cc18d16ff9661a341fb326b3aadebc5d1a83b69ed03395b318cf6666d8de9dca24e88da2a9c231395aa7780ce57ed89902717ad1266bd1551e384872719f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.7] - 2026-01-14
9
+
10
+ ### Added
11
+ - **Predecessor/Successor Search**: New `prev(key)` and `succ(key)` methods
12
+ - Returns the key-value pair immediately before/after the given key
13
+ - Works even if the key doesn't exist in the tree (returns nearest neighbor)
14
+ - Uses hash index for O(1) existence check, then O(log n) tree traversal
15
+ - **Reverse Range Queries**: All range query methods now support `:reverse` option
16
+ - `lt(key, reverse: true)`, `lte`, `gt`, `gte`, `between`
17
+ - Returns results in descending order instead of ascending
18
+ - **MultiRBTree Enhancements**:
19
+ - `get(key, last: false)` - Choose first or last value from array
20
+ - `get_first(key)`, `get_last(key)` - Convenient aliases
21
+ - `delete_one(key, last: false)` - Choose which end to delete from
22
+ - `delete_first(key)`, `delete_last(key)` - Convenient aliases
23
+ - `min(last: false)`, `max(last: false)` - Choose first or last value
24
+ - `prev(key, last: false)`, `succ(key, last: false)` - Choose first or last value
25
+
26
+ ### Changed
27
+ - **MultiRBTree Iteration**: Reverse iteration (`reverse_each`, `lt(..., reverse: true)`, etc.) now iterates each key's value array in reverse order (last to first), making it a true mirror of forward iteration
28
+
29
+ ### Fixed
30
+ - **MultiRBTree size tracking**: Fixed bug where `insert` did not increment size when key already existed in hash index
31
+
8
32
  ## [0.1.6] - 2026-01-13
9
33
 
10
34
  ### Changed
@@ -83,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
83
107
  - ASCII diagrams for tree rotation operations
84
108
  - MIT License (Copyright © 2026 Masahito Suzuki)
85
109
 
110
+ [0.1.7]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.7
86
111
  [0.1.6]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.6
87
112
  [0.1.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.5
88
113
  [0.1.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.4
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RBTree
1
+ # rbtree-ruby
2
2
 
3
3
  A pure Ruby implementation of the Red-Black Tree data structure, providing efficient ordered key-value storage with O(log n) time complexity for insertion, deletion, and lookup operations.
4
4
 
@@ -127,6 +127,66 @@ tree.nearest(7) # => [5, "five"] (same distance, returns smaller key)
127
127
  tree.nearest(8) # => [10, "ten"]
128
128
  ```
129
129
 
130
+ ### Predecessor/Successor Search
131
+
132
+ Find the next or previous key in the tree:
133
+
134
+ ```ruby
135
+ tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
136
+
137
+ tree.prev(5) # => [3, "three"] (largest key < 5)
138
+ tree.succ(5) # => [7, "seven"] (smallest key > 5)
139
+
140
+ # Works even if the key doesn't exist
141
+ tree.prev(4) # => [3, "three"] (4 doesn't exist, returns largest key < 4)
142
+ tree.succ(4) # => [5, "five"] (4 doesn't exist, returns smallest key > 4)
143
+
144
+ # Returns nil at boundaries
145
+ tree.prev(1) # => nil (no key smaller than 1)
146
+ tree.succ(7) # => nil (no key larger than 7)
147
+ ```
148
+
149
+ ### Reverse Range Queries
150
+
151
+ All range queries support a `:reverse` option to iterate in descending order:
152
+
153
+ ```ruby
154
+ tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
155
+
156
+ tree.lt(3) # => [[1, "one"], [2, "two"]]
157
+ tree.lt(3, reverse: true) # => [[2, "two"], [1, "one"]]
158
+
159
+ tree.between(1, 4, reverse: true) # => [[4, "four"], [3, "three"], [2, "two"], [1, "one"]]
160
+ ```
161
+
162
+ ### MultiRBTree Value Array Access
163
+
164
+ For keys with multiple values, choose which value to access:
165
+
166
+ ```ruby
167
+ tree = MultiRBTree.new
168
+ tree.insert(1, 'first')
169
+ tree.insert(1, 'second')
170
+ tree.insert(1, 'third')
171
+
172
+ # Access first or last value
173
+ tree.get(1) # => "first"
174
+ tree.get(1, last: true) # => "third"
175
+ tree.get_first(1) # => "first"
176
+ tree.get_last(1) # => "third"
177
+
178
+ # Delete from either end
179
+ tree.delete_first(1) # => "first"
180
+ tree.delete_last(1) # => "third"
181
+ tree.get(1) # => "second"
182
+
183
+ # min/max with :last option
184
+ tree.insert(2, 'a')
185
+ tree.insert(2, 'b')
186
+ tree.min # => [1, "second"] (first value of min key)
187
+ tree.max(last: true) # => [2, "b"] (last value of max key)
188
+ ```
189
+
130
190
  ## Performance
131
191
 
132
192
  All major operations run in **O(log n)** time:
@@ -138,6 +198,7 @@ All major operations run in **O(log n)** time:
138
198
  - `min` - **O(1)**
139
199
  - `max` - O(log n)
140
200
  - `shift` / `pop` - O(log n)
201
+ - `prev` / `succ` - O(log n) with O(1) hash check
141
202
 
142
203
  Iteration over all elements takes O(n) time.
143
204
 
@@ -147,17 +208,17 @@ RBTree uses an internal **Memory Pool** to recycle node objects.
147
208
  - Significantly reduces Garbage Collection (GC) pressure during frequent insertions and deletions (e.g., in high-throughput queues).
148
209
  - In benchmarks with 100,000 cyclic operations, **GC time was 0.0s** compared to significant pauses without pooling.
149
210
 
150
- ### RBTree vs Hash vs Array
211
+ ### RBTree vs Hash vs Array (Overwhelming Power)
151
212
 
152
- RBTree provides significant advantages for ordered operations:
213
+ For ordered and spatial operations, RBTree is not just faster—it is in a completely different class. The following benchmarks were conducted with **500,000 items**:
153
214
 
154
- | Operation | RBTree | Hash | Speedup |
155
- |-----------|--------|------|---------|
156
- | `min` / `max` | O(1) / O(log n) | O(n) | **~1000x faster** |
157
- | Range queries (`between`, `lt`, `gt`) | O(log n + k) | O(n) | **10-100x faster** |
158
- | Nearest key search | O(log n) | O(n) | **100x+ faster** |
159
- | Ordered iteration | O(n), always sorted | Requires `sort` O(n log n) | **Free sorting** |
160
- | Key lookup | O(1) | O(1) | Equal |
215
+ | Operation | RBTree | Hash/Array | Speedup | Why? |
216
+ |-----------|--------|------------|---------|------|
217
+ | **Nearest Key Search** | **O(log n)** | O(n) scan | **~8,600x faster** | Spatial binary search vs full scan |
218
+ | **Range Queries** | **O(log n + k)** | O(n) filter | **~540x faster** | Direct subtree jump vs full scan |
219
+ | **Min Extraction** | **O(log n)** | O(n) search | **~160x faster** | Continuous rebalancing vs full scan |
220
+ | **Sorted Iteration** | **O(n)** | O(n log n) | **FREE** | Always sorted vs explicit `sort` |
221
+ | **Key Lookup** | **O(1)** | O(1) | **Equal** | Optimized Hybrid Hash Index |
161
222
 
162
223
  ### When to Use RBTree
163
224
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  class RBTree
4
4
  # The version of the rbtree-ruby gem
5
- VERSION = "0.1.6"
5
+ VERSION = "0.1.7"
6
6
  end
data/lib/rbtree.rb CHANGED
@@ -329,19 +329,28 @@ class RBTree
329
329
  # Retrieves all key-value pairs with keys less than the specified key.
330
330
  #
331
331
  # @param key [Object] the upper bound (exclusive)
332
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
332
333
  # @yield [key, value] each matching key-value pair (if block given)
333
334
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
334
335
  # @example
335
336
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
336
337
  # tree.lt(3) # => [[1, "one"], [2, "two"]]
337
- # tree.lt(3) { |k, v| puts "#{k}: #{v}" } # prints pairs and returns tree
338
- def lt(key, &block)
338
+ # tree.lt(3, reverse: true) # => [[2, "two"], [1, "one"]]
339
+ def lt(key, reverse: false, &block)
339
340
  if block_given?
340
- traverse_lt(@root, key, &block)
341
+ if reverse
342
+ traverse_lt_desc(@root, key, &block)
343
+ else
344
+ traverse_lt(@root, key, &block)
345
+ end
341
346
  self
342
347
  else
343
348
  res = []
344
- traverse_lt(@root, key) { |k, v| res << [k, v] }
349
+ if reverse
350
+ traverse_lt_desc(@root, key) { |k, v| res << [k, v] }
351
+ else
352
+ traverse_lt(@root, key) { |k, v| res << [k, v] }
353
+ end
345
354
  res
346
355
  end
347
356
  end
@@ -349,18 +358,28 @@ class RBTree
349
358
  # Retrieves all key-value pairs with keys less than or equal to the specified key.
350
359
  #
351
360
  # @param key [Object] the upper bound (inclusive)
361
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
352
362
  # @yield [key, value] each matching key-value pair (if block given)
353
363
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
354
364
  # @example
355
365
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
356
366
  # tree.lte(3) # => [[1, "one"], [2, "two"], [3, "three"]]
357
- def lte(key, &block)
367
+ # tree.lte(3, reverse: true) # => [[3, "three"], [2, "two"], [1, "one"]]
368
+ def lte(key, reverse: false, &block)
358
369
  if block_given?
359
- traverse_lte(@root, key, &block)
370
+ if reverse
371
+ traverse_lte_desc(@root, key, &block)
372
+ else
373
+ traverse_lte(@root, key, &block)
374
+ end
360
375
  self
361
376
  else
362
377
  res = []
363
- traverse_lte(@root, key) { |k, v| res << [k, v] }
378
+ if reverse
379
+ traverse_lte_desc(@root, key) { |k, v| res << [k, v] }
380
+ else
381
+ traverse_lte(@root, key) { |k, v| res << [k, v] }
382
+ end
364
383
  res
365
384
  end
366
385
  end
@@ -368,18 +387,28 @@ class RBTree
368
387
  # Retrieves all key-value pairs with keys greater than the specified key.
369
388
  #
370
389
  # @param key [Object] the lower bound (exclusive)
390
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
371
391
  # @yield [key, value] each matching key-value pair (if block given)
372
392
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
373
393
  # @example
374
394
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
375
395
  # tree.gt(2) # => [[3, "three"], [4, "four"]]
376
- def gt(key, &block)
396
+ # tree.gt(2, reverse: true) # => [[4, "four"], [3, "three"]]
397
+ def gt(key, reverse: false, &block)
377
398
  if block_given?
378
- traverse_gt(@root, key, &block)
399
+ if reverse
400
+ traverse_gt_desc(@root, key, &block)
401
+ else
402
+ traverse_gt(@root, key, &block)
403
+ end
379
404
  self
380
405
  else
381
406
  res = []
382
- traverse_gt(@root, key) { |k, v| res << [k, v] }
407
+ if reverse
408
+ traverse_gt_desc(@root, key) { |k, v| res << [k, v] }
409
+ else
410
+ traverse_gt(@root, key) { |k, v| res << [k, v] }
411
+ end
383
412
  res
384
413
  end
385
414
  end
@@ -387,18 +416,28 @@ class RBTree
387
416
  # Retrieves all key-value pairs with keys greater than or equal to the specified key.
388
417
  #
389
418
  # @param key [Object] the lower bound (inclusive)
419
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
390
420
  # @yield [key, value] each matching key-value pair (if block given)
391
421
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
392
422
  # @example
393
423
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
394
424
  # tree.gte(2) # => [[2, "two"], [3, "three"], [4, "four"]]
395
- def gte(key, &block)
425
+ # tree.gte(2, reverse: true) # => [[4, "four"], [3, "three"], [2, "two"]]
426
+ def gte(key, reverse: false, &block)
396
427
  if block_given?
397
- traverse_gte(@root, key, &block)
428
+ if reverse
429
+ traverse_gte_desc(@root, key, &block)
430
+ else
431
+ traverse_gte(@root, key, &block)
432
+ end
398
433
  self
399
434
  else
400
435
  res = []
401
- traverse_gte(@root, key) { |k, v| res << [k, v] }
436
+ if reverse
437
+ traverse_gte_desc(@root, key) { |k, v| res << [k, v] }
438
+ else
439
+ traverse_gte(@root, key) { |k, v| res << [k, v] }
440
+ end
402
441
  res
403
442
  end
404
443
  end
@@ -409,20 +448,29 @@ class RBTree
409
448
  # @param max [Object] the upper bound
410
449
  # @param include_min [Boolean] whether to include the lower bound (default: true)
411
450
  # @param include_max [Boolean] whether to include the upper bound (default: true)
451
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
412
452
  # @yield [key, value] each matching key-value pair (if block given)
413
453
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
414
454
  # @example
415
455
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'})
416
456
  # tree.between(2, 4) # => [[2, "two"], [3, "three"], [4, "four"]]
417
457
  # tree.between(2, 4, include_min: false) # => [[3, "three"], [4, "four"]]
418
- # tree.between(2, 4, include_max: false) # => [[2, "two"], [3, "three"]]
419
- def between(min, max, include_min: true, include_max: true, &block)
458
+ # tree.between(2, 4, reverse: true) # => [[4, "four"], [3, "three"], [2, "two"]]
459
+ def between(min, max, include_min: true, include_max: true, reverse: false, &block)
420
460
  if block_given?
421
- traverse_between(@root, min, max, include_min, include_max, &block)
461
+ if reverse
462
+ traverse_between_desc(@root, min, max, include_min, include_max, &block)
463
+ else
464
+ traverse_between(@root, min, max, include_min, include_max, &block)
465
+ end
422
466
  self
423
467
  else
424
468
  res = []
425
- traverse_between(@root, min, max, include_min, include_max) { |k, v| res << [k, v] }
469
+ if reverse
470
+ traverse_between_desc(@root, min, max, include_min, include_max) { |k, v| res << [k, v] }
471
+ else
472
+ traverse_between(@root, min, max, include_min, include_max) { |k, v| res << [k, v] }
473
+ end
426
474
  res
427
475
  end
428
476
  end
@@ -445,6 +493,40 @@ class RBTree
445
493
  n == @nil_node ? nil : [n.key, n.value]
446
494
  end
447
495
 
496
+ # Returns the key-value pair with the largest key that is smaller than the given key.
497
+ #
498
+ # If the key exists in the tree, returns the predecessor (previous element).
499
+ # If the key does not exist, returns the largest key-value pair with key < given key.
500
+ #
501
+ # @param key [Object] the reference key
502
+ # @return [Array(Object, Object), nil] a two-element array [key, value], or nil if no predecessor exists
503
+ # @example
504
+ # tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
505
+ # tree.prev(5) # => [3, "three"]
506
+ # tree.prev(4) # => [3, "three"] (4 does not exist)
507
+ # tree.prev(1) # => nil (no predecessor)
508
+ def prev(key)
509
+ n = find_predecessor_node(key)
510
+ n == @nil_node ? nil : [n.key, n.value]
511
+ end
512
+
513
+ # Returns the key-value pair with the smallest key that is larger than the given key.
514
+ #
515
+ # If the key exists in the tree, returns the successor (next element).
516
+ # If the key does not exist, returns the smallest key-value pair with key > given key.
517
+ #
518
+ # @param key [Object] the reference key
519
+ # @return [Array(Object, Object), nil] a two-element array [key, value], or nil if no successor exists
520
+ # @example
521
+ # tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
522
+ # tree.succ(5) # => [7, "seven"]
523
+ # tree.succ(4) # => [5, "five"] (4 does not exist)
524
+ # tree.succ(7) # => nil (no successor)
525
+ def succ(key)
526
+ n = find_successor_node(key)
527
+ n == @nil_node ? nil : [n.key, n.value]
528
+ end
529
+
448
530
  # Validates the red-black tree properties.
449
531
  #
450
532
  # Checks that:
@@ -593,6 +675,98 @@ class RBTree
593
675
  end
594
676
  end
595
677
 
678
+ # Traverses nodes with keys less than the specified key in descending order.
679
+ #
680
+ # @param node [Node] the current node
681
+ # @param key [Object] the upper bound (exclusive)
682
+ # @yield [key, value] each matching key-value pair in descending order
683
+ # @return [void]
684
+ def traverse_lt_desc(node, key, &block)
685
+ return if node == @nil_node
686
+
687
+ if (node.key <=> key) < 0
688
+ traverse_lt_desc(node.right, key, &block)
689
+ yield node.key, node.value
690
+ end
691
+ traverse_lt_desc(node.left, key, &block)
692
+ end
693
+
694
+ # Traverses nodes with keys less than or equal to the specified key in descending order.
695
+ #
696
+ # @param node [Node] the current node
697
+ # @param key [Object] the upper bound (inclusive)
698
+ # @yield [key, value] each matching key-value pair in descending order
699
+ # @return [void]
700
+ def traverse_lte_desc(node, key, &block)
701
+ return if node == @nil_node
702
+
703
+ if (node.key <=> key) <= 0
704
+ traverse_lte_desc(node.right, key, &block)
705
+ yield node.key, node.value
706
+ end
707
+ traverse_lte_desc(node.left, key, &block)
708
+ end
709
+
710
+ # Traverses nodes with keys greater than the specified key in descending order.
711
+ #
712
+ # @param node [Node] the current node
713
+ # @param key [Object] the lower bound (exclusive)
714
+ # @yield [key, value] each matching key-value pair in descending order
715
+ # @return [void]
716
+ def traverse_gt_desc(node, key, &block)
717
+ return if node == @nil_node
718
+
719
+ traverse_gt_desc(node.right, key, &block)
720
+ if (node.key <=> key) > 0
721
+ yield node.key, node.value
722
+ traverse_gt_desc(node.left, key, &block)
723
+ end
724
+ end
725
+
726
+ # Traverses nodes with keys greater than or equal to the specified key in descending order.
727
+ #
728
+ # @param node [Node] the current node
729
+ # @param key [Object] the lower bound (inclusive)
730
+ # @yield [key, value] each matching key-value pair in descending order
731
+ # @return [void]
732
+ def traverse_gte_desc(node, key, &block)
733
+ return if node == @nil_node
734
+
735
+ traverse_gte_desc(node.right, key, &block)
736
+ if (node.key <=> key) >= 0
737
+ yield node.key, node.value
738
+ traverse_gte_desc(node.left, key, &block)
739
+ end
740
+ end
741
+
742
+ # Traverses nodes with keys within the specified range in descending order.
743
+ #
744
+ # @param node [Node] the current node
745
+ # @param min [Object] the lower bound
746
+ # @param max [Object] the upper bound
747
+ # @param include_min [Boolean] whether to include the lower bound
748
+ # @param include_max [Boolean] whether to include the upper bound
749
+ # @yield [key, value] each matching key-value pair in descending order
750
+ # @return [void]
751
+ def traverse_between_desc(node, min, max, include_min, include_max, &block)
752
+ return if node == @nil_node
753
+
754
+ if (node.key <=> max) < 0
755
+ traverse_between_desc(node.right, min, max, include_min, include_max, &block)
756
+ end
757
+
758
+ greater = include_min ? (node.key <=> min) >= 0 : (node.key <=> min) > 0
759
+ less = include_max ? (node.key <=> max) <= 0 : (node.key <=> max) < 0
760
+
761
+ if greater && less
762
+ yield node.key, node.value
763
+ end
764
+
765
+ if (node.key <=> min) > 0
766
+ traverse_between_desc(node.left, min, max, include_min, include_max, &block)
767
+ end
768
+ end
769
+
596
770
  # Restores red-black tree properties after insertion.
597
771
  #
598
772
  # This method fixes any violations of red-black properties that may occur
@@ -842,6 +1016,86 @@ class RBTree
842
1016
  closest
843
1017
  end
844
1018
 
1019
+ # Finds the node with the largest key that is smaller than the given key.
1020
+ #
1021
+ # If the key exists in the tree, returns its predecessor node.
1022
+ # If the key does not exist, returns the largest node with key < given key.
1023
+ #
1024
+ # @param key [Object] the reference key
1025
+ # @return [Node] the predecessor node, or @nil_node if none exists
1026
+ def find_predecessor_node(key)
1027
+ # Check if key exists using O(1) hash lookup
1028
+ if (node = @hash_index[key])
1029
+ # Key exists: find predecessor in subtree or ancestors
1030
+ if node.left != @nil_node
1031
+ return rightmost(node.left)
1032
+ else
1033
+ # Walk up to find first ancestor where we came from the right
1034
+ current = node
1035
+ parent = current.parent
1036
+ while parent != @nil_node && current == parent.left
1037
+ current = parent
1038
+ parent = parent.parent
1039
+ end
1040
+ return parent
1041
+ end
1042
+ end
1043
+
1044
+ # Key doesn't exist: descend tree tracking the best candidate
1045
+ current = @root
1046
+ predecessor = @nil_node
1047
+ while current != @nil_node
1048
+ cmp = key <=> current.key
1049
+ if cmp > 0
1050
+ predecessor = current # This node's key < given key
1051
+ current = current.right
1052
+ else
1053
+ current = current.left
1054
+ end
1055
+ end
1056
+ predecessor
1057
+ end
1058
+
1059
+ # Finds the node with the smallest key that is larger than the given key.
1060
+ #
1061
+ # If the key exists in the tree, returns its successor node.
1062
+ # If the key does not exist, returns the smallest node with key > given key.
1063
+ #
1064
+ # @param key [Object] the reference key
1065
+ # @return [Node] the successor node, or @nil_node if none exists
1066
+ def find_successor_node(key)
1067
+ # Check if key exists using O(1) hash lookup
1068
+ if (node = @hash_index[key])
1069
+ # Key exists: find successor in subtree or ancestors
1070
+ if node.right != @nil_node
1071
+ return leftmost(node.right)
1072
+ else
1073
+ # Walk up to find first ancestor where we came from the left
1074
+ current = node
1075
+ parent = current.parent
1076
+ while parent != @nil_node && current == parent.right
1077
+ current = parent
1078
+ parent = parent.parent
1079
+ end
1080
+ return parent
1081
+ end
1082
+ end
1083
+
1084
+ # Key doesn't exist: descend tree tracking the best candidate
1085
+ current = @root
1086
+ successor = @nil_node
1087
+ while current != @nil_node
1088
+ cmp = key <=> current.key
1089
+ if cmp < 0
1090
+ successor = current # This node's key > given key
1091
+ current = current.left
1092
+ else
1093
+ current = current.right
1094
+ end
1095
+ end
1096
+ successor
1097
+ end
1098
+
845
1099
  # Finds the leftmost (minimum) node in a subtree.
846
1100
  #
847
1101
  # @param node [Node] the root of the subtree
@@ -994,7 +1248,39 @@ end
994
1248
  # * Multiple values per key using arrays
995
1249
  # * Separate methods for single deletion (`delete_one`) vs. all deletions (`delete`)
996
1250
  # * Values for each key maintain insertion order
997
- # * min/max return first/last values respectively
1251
+ # * Configurable access to first or last value via `:last` option
1252
+ #
1253
+ # == Value Array Access
1254
+ #
1255
+ # For each key, values are stored in insertion order. Methods that access
1256
+ # a single value support a `:last` option to choose which end of the array:
1257
+ #
1258
+ # * +get(key)+, +get_first(key)+ - returns first value (oldest)
1259
+ # * +get(key, last: true)+, +get_last(key)+ - returns last value (newest)
1260
+ # * +delete_one(key)+, +delete_first(key)+ - removes first value
1261
+ # * +delete_one(key, last: true)+, +delete_last(key)+ - removes last value
1262
+ # * +prev(key)+, +succ(key)+ - returns first value of adjacent key
1263
+ # * +prev(key, last: true)+, +succ(key, last: true)+ - returns last value
1264
+ #
1265
+ # == Boundary Operations
1266
+ #
1267
+ # * +min+, +max+ - return [key, first_value] by default
1268
+ # * +min(last: true)+, +max(last: true)+ - return [key, last_value]
1269
+ # * +shift+ - removes and returns [smallest_key, first_value]
1270
+ # * +pop+ - removes and returns [largest_key, last_value]
1271
+ #
1272
+ # == Iteration Order
1273
+ #
1274
+ # When iterating over values, the order depends on the direction:
1275
+ #
1276
+ # * Forward iteration (+each+, +lt+, +gt+, etc.): Each key's values are
1277
+ # yielded in insertion order (first to last).
1278
+ #
1279
+ # * Reverse iteration (+reverse_each+, +lt(key, reverse: true)+, etc.):
1280
+ # Each key's values are yielded in reverse insertion order (last to first).
1281
+ #
1282
+ # This ensures consistent behavior where reverse iteration is truly the
1283
+ # mirror image of forward iteration.
998
1284
  #
999
1285
  # == Usage
1000
1286
  #
@@ -1003,32 +1289,55 @@ end
1003
1289
  # tree.insert(1, 'second one')
1004
1290
  # tree.insert(2, 'two')
1005
1291
  #
1006
- # tree.size # => 3 (total key-value pairs)
1007
- # tree.get(1) # => "first one" (first value)
1008
- # tree.get_all(1) # => ["first one", "second one"] (all values)
1292
+ # tree.size # => 3 (total key-value pairs)
1293
+ # tree.get(1) # => "first one" (first value)
1294
+ # tree.get(1, last: true) # => "second one" (last value)
1295
+ # tree.get_all(1) # => ["first one", "second one"] (all values)
1009
1296
  #
1010
- # tree.delete_one(1) # removes only "first one"
1011
- # tree.get(1) # => "second one"
1297
+ # tree.delete_one(1) # removes only "first one"
1298
+ # tree.get(1) # => "second one"
1012
1299
  #
1013
- # tree.delete(1) # removes all remaining values for key 1
1300
+ # tree.delete(1) # removes all remaining values for key 1
1014
1301
  #
1015
1302
  # @author Masahito Suzuki
1016
1303
  # @since 0.1.2
1017
1304
  class MultiRBTree < RBTree
1018
- # Retrieves the first value associated with the given key.
1305
+ # Retrieves a value associated with the given key.
1019
1306
  #
1020
1307
  # @param key [Object] the key to look up
1021
- # @return [Object, nil] the first value for the key, or nil if not found
1308
+ # @param last [Boolean] if true, return the last value; otherwise return the first (default: false)
1309
+ # @return [Object, nil] the value for the key, or nil if not found
1022
1310
  # @example
1023
1311
  # tree = MultiRBTree.new
1024
1312
  # tree.insert(1, 'first')
1025
1313
  # tree.insert(1, 'second')
1026
- # tree.get(1) # => "first"
1027
- def get(key)
1314
+ # tree.get(1) # => "first"
1315
+ # tree.get(1, last: true) # => "second"
1316
+ def get(key, last: false)
1028
1317
  n = find_node(key)
1029
- n == @nil_node || n.value.empty? ? nil : n.value.first
1318
+ return nil if n == @nil_node || n.value.empty?
1319
+ last ? n.value.last : n.value.first
1030
1320
  end
1031
- alias_method :[], :get
1321
+
1322
+ # Retrieves the first value associated with the given key.
1323
+ #
1324
+ # @param key [Object] the key to look up
1325
+ # @return [Object, nil] the first value for the key, or nil if not found
1326
+ def get_first(key) = get(key, last: false)
1327
+
1328
+ # Retrieves the last value associated with the given key.
1329
+ #
1330
+ # @param key [Object] the key to look up
1331
+ # @return [Object, nil] the last value for the key, or nil if not found
1332
+ def get_last(key) = get(key, last: true)
1333
+
1334
+ # Returns the first value for the given key (for Hash-like access).
1335
+ #
1336
+ # Note: Unlike get(), this method does not accept options.
1337
+ #
1338
+ # @param key [Object] the key to look up
1339
+ # @return [Object, nil] the first value for the key, or nil if not found
1340
+ def [](key) = get(key)
1032
1341
 
1033
1342
  # Retrieves all values associated with the given key.
1034
1343
  #
@@ -1059,6 +1368,7 @@ class MultiRBTree < RBTree
1059
1368
  def insert(key, value)
1060
1369
  if (node = @hash_index[key])
1061
1370
  node.value << value
1371
+ @size += 1
1062
1372
  return true
1063
1373
  end
1064
1374
  y = @nil_node
@@ -1099,24 +1409,25 @@ class MultiRBTree < RBTree
1099
1409
  true
1100
1410
  end
1101
1411
 
1102
- # Deletes only the first value for the specified key.
1412
+ # Deletes a single value for the specified key.
1103
1413
  #
1104
- # If the key has multiple values, removes only the first one.
1414
+ # If the key has multiple values, removes only one value.
1105
1415
  # If this was the last value for the key, the node is removed from the tree.
1106
1416
  #
1107
1417
  # @param key [Object] the key to delete from
1418
+ # @param last [Boolean] if true, remove the last value; otherwise remove the first (default: false)
1108
1419
  # @return [Object, nil] the deleted value, or nil if key not found
1109
1420
  # @example
1110
1421
  # tree = MultiRBTree.new
1111
1422
  # tree.insert(1, 'first')
1112
1423
  # tree.insert(1, 'second')
1113
- # tree.delete_one(1) # => "first"
1114
- # tree.get(1) # => "second"
1115
- def delete_one(key)
1424
+ # tree.delete_one(1) # => "first"
1425
+ # tree.delete_one(1, last: true) # => "second" (if more values existed)
1426
+ def delete_one(key, last: false)
1116
1427
  z = @hash_index[key] # O(1) lookup
1117
1428
  return nil unless z
1118
1429
 
1119
- value = z.value.shift
1430
+ value = last ? z.value.pop : z.value.shift
1120
1431
  @size -= 1
1121
1432
  if z.value.empty?
1122
1433
  @hash_index.delete(key) # Remove from index when node removed
@@ -1125,6 +1436,18 @@ class MultiRBTree < RBTree
1125
1436
  value
1126
1437
  end
1127
1438
 
1439
+ # Deletes the first value for the specified key.
1440
+ #
1441
+ # @param key [Object] the key to delete from
1442
+ # @return [Object, nil] the deleted value, or nil if key not found
1443
+ def delete_first(key) = delete_one(key, last: false)
1444
+
1445
+ # Deletes the last value for the specified key.
1446
+ #
1447
+ # @param key [Object] the key to delete from
1448
+ # @return [Object, nil] the deleted value, or nil if key not found
1449
+ def delete_last(key) = delete_one(key, last: true)
1450
+
1128
1451
  # Deletes all values for the specified key.
1129
1452
  #
1130
1453
  # Removes the node and all associated values.
@@ -1172,18 +1495,32 @@ class MultiRBTree < RBTree
1172
1495
  [n.key, val]
1173
1496
  end
1174
1497
 
1175
- def min
1498
+ def min(last: false)
1176
1499
  return nil if @min_node == @nil_node || @min_node.value.empty?
1177
- [@min_node.key, @min_node.value.first]
1500
+ [@min_node.key, last ? @min_node.value.last : @min_node.value.first]
1178
1501
  end
1179
1502
 
1180
- def max
1503
+ def max(last: false)
1181
1504
  n = rightmost(@root)
1182
- n == @nil_node || n.value.empty? ? nil : [n.key, n.value.first]
1505
+ return nil if n == @nil_node || n.value.empty?
1506
+ [n.key, last ? n.value.last : n.value.first]
1183
1507
  end
1184
1508
 
1185
1509
  def nearest(key)
1186
- (n = super(key)) ? [n[0], n[1].first] : nil
1510
+ n = find_nearest_node(key)
1511
+ n == @nil_node || n.value.empty? ? nil : [n.key, n.value.first]
1512
+ end
1513
+
1514
+ def prev(key, last: false)
1515
+ n = find_predecessor_node(key)
1516
+ return nil if n == @nil_node || n.value.empty?
1517
+ [n.key, last ? n.value.last : n.value.first]
1518
+ end
1519
+
1520
+ def succ(key, last: false)
1521
+ n = find_successor_node(key)
1522
+ return nil if n == @nil_node || n.value.empty?
1523
+ [n.key, last ? n.value.last : n.value.first]
1187
1524
  end
1188
1525
 
1189
1526
  private
@@ -1273,6 +1610,65 @@ class MultiRBTree < RBTree
1273
1610
  traverse_between(node.right, min, max, include_min, include_max, &block)
1274
1611
  end
1275
1612
  end
1613
+
1614
+ def traverse_lt_desc(node, key, &block)
1615
+ return if node == @nil_node
1616
+
1617
+ if (node.key <=> key) < 0
1618
+ traverse_lt_desc(node.right, key, &block)
1619
+ node.value.reverse_each { |v| yield node.key, v }
1620
+ end
1621
+ traverse_lt_desc(node.left, key, &block)
1622
+ end
1623
+
1624
+ def traverse_lte_desc(node, key, &block)
1625
+ return if node == @nil_node
1626
+
1627
+ if (node.key <=> key) <= 0
1628
+ traverse_lte_desc(node.right, key, &block)
1629
+ node.value.reverse_each { |v| yield node.key, v }
1630
+ end
1631
+ traverse_lte_desc(node.left, key, &block)
1632
+ end
1633
+
1634
+ def traverse_gt_desc(node, key, &block)
1635
+ return if node == @nil_node
1636
+
1637
+ traverse_gt_desc(node.right, key, &block)
1638
+ if (node.key <=> key) > 0
1639
+ node.value.reverse_each { |v| yield node.key, v }
1640
+ traverse_gt_desc(node.left, key, &block)
1641
+ end
1642
+ end
1643
+
1644
+ def traverse_gte_desc(node, key, &block)
1645
+ return if node == @nil_node
1646
+
1647
+ traverse_gte_desc(node.right, key, &block)
1648
+ if (node.key <=> key) >= 0
1649
+ node.value.reverse_each { |v| yield node.key, v }
1650
+ traverse_gte_desc(node.left, key, &block)
1651
+ end
1652
+ end
1653
+
1654
+ def traverse_between_desc(node, min, max, include_min, include_max, &block)
1655
+ return if node == @nil_node
1656
+
1657
+ if (node.key <=> max) < 0
1658
+ traverse_between_desc(node.right, min, max, include_min, include_max, &block)
1659
+ end
1660
+
1661
+ greater = include_min ? (node.key <=> min) >= 0 : (node.key <=> min) > 0
1662
+ less = include_max ? (node.key <=> max) <= 0 : (node.key <=> max) < 0
1663
+
1664
+ if greater && less
1665
+ node.value.reverse_each { |v| yield node.key, v }
1666
+ end
1667
+
1668
+ if (node.key <=> min) > 0
1669
+ traverse_between_desc(node.left, min, max, include_min, include_max, &block)
1670
+ end
1671
+ end
1276
1672
  end
1277
1673
 
1278
1674
  # Internal node structure for RBTree.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtree-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahito Suzuki
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  requirements: []
50
- rubygems_version: 3.6.9
50
+ rubygems_version: 4.0.3
51
51
  specification_version: 4
52
52
  summary: A pure Ruby implementation of Red-Black Tree with multi-value support
53
53
  test_files: []