rbtree-ruby 0.1.5 → 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: 56ddd66545f9e423b96066b12deea8e22fc86b35b7a97be056ebf91f80543e77
4
- data.tar.gz: 10a3ec88b0a5f5cb199ac96267d7665bfee99ab4fb9cf89488790fe798120b05
3
+ metadata.gz: 8aa1d883925f6b7f608108f1f916598aa04be013aee415c06dfc7bda35d6986c
4
+ data.tar.gz: e935f213100c933b42a3cf0d709809d5ca8d536db56d6e27a6b3de9681847ad9
5
5
  SHA512:
6
- metadata.gz: 1d521a30299f8c3db327d1b671d84113cf3c9e5adc90a78747e76b7480ef02ceb95d813a90eeebadfd32e4b8360b2d43fffd1ed1ac4d9314d8368cbe137ce7aa
7
- data.tar.gz: 445a6cf0a99efdd0b5b4dc41dd86ec219e784cc3a2ecf150b4467e2619bcb08bd9b47d119d1c5310f43fdecdd7dc19941c3bfa4ae9e7b68d2b359819336f0a97
6
+ metadata.gz: 419f0270137b6a6ae39bd73d5ea0c4cc25ff0769c288619ee795d172e724040c6032c09c952745b9bb871ff082861c00ae879fa6a98a571414bad87c2534cb9a
7
+ data.tar.gz: e403cc18d16ff9661a341fb326b3aadebc5d1a83b69ed03395b318cf6666d8de9dca24e88da2a9c231395aa7780ce57ed89902717ad1266bd1551e384872719f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ 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
+
32
+ ## [0.1.6] - 2026-01-13
33
+
34
+ ### Changed
35
+ - **Performance**: Standardized on Boolean colors (`true`/`false`) instead of Symbols (`:red`/`:black`) for faster checks.
36
+ - **Optimization**: `insert` operations now check the internal hash index first, allowing O(1) updates for existing keys (RBTree) and O(1) appends (MultiRBTree).
37
+
8
38
  ## [0.1.5] - 2026-01-13
9
39
 
10
40
  ### Changed
@@ -77,6 +107,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
107
  - ASCII diagrams for tree rotation operations
78
108
  - MIT License (Copyright © 2026 Masahito Suzuki)
79
109
 
110
+ [0.1.7]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.7
111
+ [0.1.6]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.6
80
112
  [0.1.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.5
81
113
  [0.1.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.4
82
114
  [0.1.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.3
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.5"
5
+ VERSION = "0.1.7"
6
6
  end
data/lib/rbtree.rb CHANGED
@@ -174,6 +174,11 @@ class RBTree
174
174
  # tree.insert(1, 'uno', overwrite: false) # => nil (no change)
175
175
  # tree[2] = 'two' # using alias
176
176
  def insert(key, value, overwrite: true)
177
+ if (node = @hash_index[key])
178
+ return nil unless overwrite
179
+ node.value = value
180
+ return true
181
+ end
177
182
  y = @nil_node
178
183
  x = @root
179
184
  while x != @nil_node
@@ -324,19 +329,28 @@ class RBTree
324
329
  # Retrieves all key-value pairs with keys less than the specified key.
325
330
  #
326
331
  # @param key [Object] the upper bound (exclusive)
332
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
327
333
  # @yield [key, value] each matching key-value pair (if block given)
328
334
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
329
335
  # @example
330
336
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
331
337
  # tree.lt(3) # => [[1, "one"], [2, "two"]]
332
- # tree.lt(3) { |k, v| puts "#{k}: #{v}" } # prints pairs and returns tree
333
- def lt(key, &block)
338
+ # tree.lt(3, reverse: true) # => [[2, "two"], [1, "one"]]
339
+ def lt(key, reverse: false, &block)
334
340
  if block_given?
335
- 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
336
346
  self
337
347
  else
338
348
  res = []
339
- 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
340
354
  res
341
355
  end
342
356
  end
@@ -344,18 +358,28 @@ class RBTree
344
358
  # Retrieves all key-value pairs with keys less than or equal to the specified key.
345
359
  #
346
360
  # @param key [Object] the upper bound (inclusive)
361
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
347
362
  # @yield [key, value] each matching key-value pair (if block given)
348
363
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
349
364
  # @example
350
365
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
351
366
  # tree.lte(3) # => [[1, "one"], [2, "two"], [3, "three"]]
352
- def lte(key, &block)
367
+ # tree.lte(3, reverse: true) # => [[3, "three"], [2, "two"], [1, "one"]]
368
+ def lte(key, reverse: false, &block)
353
369
  if block_given?
354
- 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
355
375
  self
356
376
  else
357
377
  res = []
358
- 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
359
383
  res
360
384
  end
361
385
  end
@@ -363,18 +387,28 @@ class RBTree
363
387
  # Retrieves all key-value pairs with keys greater than the specified key.
364
388
  #
365
389
  # @param key [Object] the lower bound (exclusive)
390
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
366
391
  # @yield [key, value] each matching key-value pair (if block given)
367
392
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
368
393
  # @example
369
394
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
370
395
  # tree.gt(2) # => [[3, "three"], [4, "four"]]
371
- def gt(key, &block)
396
+ # tree.gt(2, reverse: true) # => [[4, "four"], [3, "three"]]
397
+ def gt(key, reverse: false, &block)
372
398
  if block_given?
373
- 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
374
404
  self
375
405
  else
376
406
  res = []
377
- 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
378
412
  res
379
413
  end
380
414
  end
@@ -382,18 +416,28 @@ class RBTree
382
416
  # Retrieves all key-value pairs with keys greater than or equal to the specified key.
383
417
  #
384
418
  # @param key [Object] the lower bound (inclusive)
419
+ # @param reverse [Boolean] if true, iterate in descending order (default: false)
385
420
  # @yield [key, value] each matching key-value pair (if block given)
386
421
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
387
422
  # @example
388
423
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
389
424
  # tree.gte(2) # => [[2, "two"], [3, "three"], [4, "four"]]
390
- def gte(key, &block)
425
+ # tree.gte(2, reverse: true) # => [[4, "four"], [3, "three"], [2, "two"]]
426
+ def gte(key, reverse: false, &block)
391
427
  if block_given?
392
- 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
393
433
  self
394
434
  else
395
435
  res = []
396
- 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
397
441
  res
398
442
  end
399
443
  end
@@ -404,20 +448,29 @@ class RBTree
404
448
  # @param max [Object] the upper bound
405
449
  # @param include_min [Boolean] whether to include the lower bound (default: true)
406
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)
407
452
  # @yield [key, value] each matching key-value pair (if block given)
408
453
  # @return [Array<Array(Object, Object)>, RBTree] array of [key, value] pairs if no block, self otherwise
409
454
  # @example
410
455
  # tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'})
411
456
  # tree.between(2, 4) # => [[2, "two"], [3, "three"], [4, "four"]]
412
457
  # tree.between(2, 4, include_min: false) # => [[3, "three"], [4, "four"]]
413
- # tree.between(2, 4, include_max: false) # => [[2, "two"], [3, "three"]]
414
- 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)
415
460
  if block_given?
416
- 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
417
466
  self
418
467
  else
419
468
  res = []
420
- 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
421
474
  res
422
475
  end
423
476
  end
@@ -440,6 +493,40 @@ class RBTree
440
493
  n == @nil_node ? nil : [n.key, n.value]
441
494
  end
442
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
+
443
530
  # Validates the red-black tree properties.
444
531
  #
445
532
  # Checks that:
@@ -588,6 +675,98 @@ class RBTree
588
675
  end
589
676
  end
590
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
+
591
770
  # Restores red-black tree properties after insertion.
592
771
  #
593
772
  # This method fixes any violations of red-black properties that may occur
@@ -837,6 +1016,86 @@ class RBTree
837
1016
  closest
838
1017
  end
839
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
+
840
1099
  # Finds the leftmost (minimum) node in a subtree.
841
1100
  #
842
1101
  # @param node [Node] the root of the subtree
@@ -989,7 +1248,39 @@ end
989
1248
  # * Multiple values per key using arrays
990
1249
  # * Separate methods for single deletion (`delete_one`) vs. all deletions (`delete`)
991
1250
  # * Values for each key maintain insertion order
992
- # * 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.
993
1284
  #
994
1285
  # == Usage
995
1286
  #
@@ -998,32 +1289,55 @@ end
998
1289
  # tree.insert(1, 'second one')
999
1290
  # tree.insert(2, 'two')
1000
1291
  #
1001
- # tree.size # => 3 (total key-value pairs)
1002
- # tree.get(1) # => "first one" (first value)
1003
- # 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)
1004
1296
  #
1005
- # tree.delete_one(1) # removes only "first one"
1006
- # tree.get(1) # => "second one"
1297
+ # tree.delete_one(1) # removes only "first one"
1298
+ # tree.get(1) # => "second one"
1007
1299
  #
1008
- # tree.delete(1) # removes all remaining values for key 1
1300
+ # tree.delete(1) # removes all remaining values for key 1
1009
1301
  #
1010
1302
  # @author Masahito Suzuki
1011
1303
  # @since 0.1.2
1012
1304
  class MultiRBTree < RBTree
1013
- # Retrieves the first value associated with the given key.
1305
+ # Retrieves a value associated with the given key.
1014
1306
  #
1015
1307
  # @param key [Object] the key to look up
1016
- # @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
1017
1310
  # @example
1018
1311
  # tree = MultiRBTree.new
1019
1312
  # tree.insert(1, 'first')
1020
1313
  # tree.insert(1, 'second')
1021
- # tree.get(1) # => "first"
1022
- def get(key)
1314
+ # tree.get(1) # => "first"
1315
+ # tree.get(1, last: true) # => "second"
1316
+ def get(key, last: false)
1023
1317
  n = find_node(key)
1024
- 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
1025
1320
  end
1026
- 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)
1027
1341
 
1028
1342
  # Retrieves all values associated with the given key.
1029
1343
  #
@@ -1052,6 +1366,11 @@ class MultiRBTree < RBTree
1052
1366
  # tree.insert(1, 'first')
1053
1367
  # tree.insert(1, 'second') # adds another value for key 1
1054
1368
  def insert(key, value)
1369
+ if (node = @hash_index[key])
1370
+ node.value << value
1371
+ @size += 1
1372
+ return true
1373
+ end
1055
1374
  y = @nil_node
1056
1375
  x = @root
1057
1376
  while x != @nil_node
@@ -1090,24 +1409,25 @@ class MultiRBTree < RBTree
1090
1409
  true
1091
1410
  end
1092
1411
 
1093
- # Deletes only the first value for the specified key.
1412
+ # Deletes a single value for the specified key.
1094
1413
  #
1095
- # If the key has multiple values, removes only the first one.
1414
+ # If the key has multiple values, removes only one value.
1096
1415
  # If this was the last value for the key, the node is removed from the tree.
1097
1416
  #
1098
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)
1099
1419
  # @return [Object, nil] the deleted value, or nil if key not found
1100
1420
  # @example
1101
1421
  # tree = MultiRBTree.new
1102
1422
  # tree.insert(1, 'first')
1103
1423
  # tree.insert(1, 'second')
1104
- # tree.delete_one(1) # => "first"
1105
- # tree.get(1) # => "second"
1106
- 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)
1107
1427
  z = @hash_index[key] # O(1) lookup
1108
1428
  return nil unless z
1109
1429
 
1110
- value = z.value.shift
1430
+ value = last ? z.value.pop : z.value.shift
1111
1431
  @size -= 1
1112
1432
  if z.value.empty?
1113
1433
  @hash_index.delete(key) # Remove from index when node removed
@@ -1116,6 +1436,18 @@ class MultiRBTree < RBTree
1116
1436
  value
1117
1437
  end
1118
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
+
1119
1451
  # Deletes all values for the specified key.
1120
1452
  #
1121
1453
  # Removes the node and all associated values.
@@ -1163,18 +1495,32 @@ class MultiRBTree < RBTree
1163
1495
  [n.key, val]
1164
1496
  end
1165
1497
 
1166
- def min
1498
+ def min(last: false)
1167
1499
  return nil if @min_node == @nil_node || @min_node.value.empty?
1168
- [@min_node.key, @min_node.value.first]
1500
+ [@min_node.key, last ? @min_node.value.last : @min_node.value.first]
1169
1501
  end
1170
1502
 
1171
- def max
1503
+ def max(last: false)
1172
1504
  n = rightmost(@root)
1173
- 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]
1174
1507
  end
1175
1508
 
1176
1509
  def nearest(key)
1177
- (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]
1178
1524
  end
1179
1525
 
1180
1526
  private
@@ -1264,6 +1610,65 @@ class MultiRBTree < RBTree
1264
1610
  traverse_between(node.right, min, max, include_min, include_max, &block)
1265
1611
  end
1266
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
1267
1672
  end
1268
1673
 
1269
1674
  # Internal node structure for RBTree.
@@ -1288,16 +1693,16 @@ end
1288
1693
  class RBTree::Node
1289
1694
  attr_accessor :key, :value, :color, :left, :right, :parent
1290
1695
 
1291
- # Red color constant
1292
- RED = :red
1293
- # Black color constant
1294
- BLACK = :black
1696
+ # Red color constant (true)
1697
+ RED = true
1698
+ # Black color constant (false)
1699
+ BLACK = false
1295
1700
 
1296
1701
  # Creates a new Node.
1297
1702
  #
1298
1703
  # @param key [Object] the key
1299
1704
  # @param value [Object] the value
1300
- # @param color [Symbol] the color (:red or :black)
1705
+ # @param color [Boolean] the color (true=red, false=black)
1301
1706
  # @param left [Node] the left child
1302
1707
  # @param right [Node] the right child
1303
1708
  # @param parent [Node] the parent node
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.5
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: []