rbtree-ruby 0.3.2 → 0.3.4
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 +4 -4
- data/CHANGELOG.md +23 -0
- data/README.ja.md +16 -6
- data/README.md +16 -6
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +40 -12
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 911e435d0a205afc1a62791f857ac66d6d6300d2aa332566aa916e65e16148d2
|
|
4
|
+
data.tar.gz: d1a2b2aec3404901985cfec8a54c5b3df87d12bd4c8872833ba111dd3641033b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b1a96900494faa73b223e5905897299799c9a5432aa6cbdd95c6881c48c8666fe82f1653907231918fd027413eed3036da64a21087bfded6307a434c3de63b2
|
|
7
|
+
data.tar.gz: 6456acef3636bf7f857611a22407c4f4a053c632a973b2dfc05913d99b55a338c3a4dbce41d03074c7589354cc4bad2765c5e4be683d2a080248ea30b6a22b04
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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.3.4] - 2026-01-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Range-based Lookup**: Support for passing `Range` objects to the bracket operator `[]`.
|
|
12
|
+
- `tree[2..4]` -> `between(2, 4)`
|
|
13
|
+
- `tree[2...4]` -> `between(2, 4, include_max: false)`
|
|
14
|
+
- `tree[5..]` -> `gte(5)`
|
|
15
|
+
- `tree[..10]` -> `lte(10)`
|
|
16
|
+
- `tree[...10]` -> `lt(10)`
|
|
17
|
+
- Returns an `Enumerator` of `[key, value]` pairs.
|
|
18
|
+
- *Note: `gt` (greater than exclusive) is not covered by standard Ruby Range syntax and remains available via the `gt` method.*
|
|
19
|
+
|
|
20
|
+
## [0.3.3] - 2026-01-23
|
|
21
|
+
|
|
22
|
+
### Optimized
|
|
23
|
+
- **find_successor_node**: Optimized performance for scans starting from the beginning (or before `min_key`) when the `safe: true` option is used. Added a fast-path (short-circuit) when the given key is smaller than `min_key`.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- **MultiRBTree Aliases**: Fixed `delete`, `get`, and `[]` aliases in `MultiRBTree`. They now correctly point to the overridden methods in the derived class, ensuring correct value handling and size tracking.
|
|
27
|
+
- **enum_for Syntax**: Syntactic corrections for `enum_for` to ensure robust keyword argument handling in enumerators.
|
|
28
|
+
|
|
8
29
|
## [0.3.2] - 2026-01-15
|
|
9
30
|
|
|
10
31
|
### Added
|
|
@@ -198,6 +219,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
198
219
|
- ASCII diagrams for tree rotation operations
|
|
199
220
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
200
221
|
|
|
222
|
+
[0.3.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.4
|
|
223
|
+
[0.3.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.3
|
|
201
224
|
[0.3.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.2
|
|
202
225
|
[0.3.1]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.1
|
|
203
226
|
[0.3.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.0
|
data/README.ja.md
CHANGED
|
@@ -6,11 +6,14 @@ Red-Black Tree(赤黒木)データ構造のピュアRuby実装です。挿
|
|
|
6
6
|
|
|
7
7
|
## 特徴
|
|
8
8
|
|
|
9
|
-
- **自己平衡二分探索木**:
|
|
10
|
-
- **順序付き操作**:
|
|
11
|
-
- **複数値サポート**: `MultiRBTree
|
|
12
|
-
- **ピュアRuby**: C
|
|
13
|
-
-
|
|
9
|
+
- **自己平衡二分探索木**: 赤黒木の性質により最適なパフォーマンスを維持。挿入・削除・検索がすべてO(log n)。
|
|
10
|
+
- **順序付き操作**: ソート済みイテレーション、範囲クエリ(lt, gt, between)、最小/最大値取得が高速に実行可能。
|
|
11
|
+
- **複数値サポート**: `MultiRBTree`クラスで同一キーに複数の値を格納。値は挿入順に保持され、最初または最後の値を個別にアクセス可能。
|
|
12
|
+
- **ピュアRuby**: C拡張不要。MRI, JRuby, TruffleRubyなどあらゆるRuby実装で動作。
|
|
13
|
+
- **ハイブリッドインデックス**: 内部ハッシュインデックスにより、キー検索と存在確認がO(1)の超高速アクセスを実現。
|
|
14
|
+
- **メモリ効率**: ノードプールによるオブジェクト再利用と自動縮小機能でGC負荷を大幅に削減。長時間実行アプリにも適応。
|
|
15
|
+
- **最近傍検索**: 数値キーに対して、最も近いキーペアをO(log n)で効率的に探索。
|
|
16
|
+
- **安全なイテレーション**: `safe: true`オプションにより、イテレーション中に他の操作(削除・挿入)を安全に実行可能。
|
|
14
17
|
|
|
15
18
|
## インストール
|
|
16
19
|
|
|
@@ -77,9 +80,16 @@ tree.max # => [20, "twenty"]
|
|
|
77
80
|
|
|
78
81
|
# 範囲クエリ(Enumeratorを返す、配列には.to_aを使用)
|
|
79
82
|
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
80
|
-
tree.gte(10).
|
|
83
|
+
tree.gte(10).each { |k, v| puts k } # ブロック渡しでのループ
|
|
81
84
|
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
82
85
|
|
|
86
|
+
# []でのRangeオブジェクトの利用 (v0.3.4+)
|
|
87
|
+
tree[..10].to_a # lte(10)相当
|
|
88
|
+
tree[2..10].each { |k, v| ... } # Rangeでのループ
|
|
89
|
+
tree[2...10].to_a # between(2, 10, include_max: false)相当
|
|
90
|
+
tree[10..].to_a # gte(10)相当
|
|
91
|
+
tree[2..10, reverse: true].to_a # オプション指定可能
|
|
92
|
+
|
|
83
93
|
# shiftとpop
|
|
84
94
|
tree.shift # => [1, "one"] (最小値を削除)
|
|
85
95
|
tree.pop # => [20, "twenty"] (最大値を削除)
|
data/README.md
CHANGED
|
@@ -6,11 +6,14 @@ A pure Ruby implementation of the Red-Black Tree data structure, providing effic
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Self-Balancing Binary Search Tree**: Maintains optimal performance through red-black tree properties
|
|
10
|
-
- **Ordered Operations**: Efficient range queries, min/max retrieval
|
|
11
|
-
- **Multi-Value Support**: `MultiRBTree` class
|
|
12
|
-
- **Pure Ruby**: No C extensions required
|
|
13
|
-
- **
|
|
9
|
+
- **Self-Balancing Binary Search Tree**: Maintains optimal performance through red-black tree properties. All insertions, deletions, and lookups run in O(log n).
|
|
10
|
+
- **Ordered Operations**: Efficient sorted iteration, range queries (`lt`, `gt`, `between`), min/max retrieval.
|
|
11
|
+
- **Multi-Value Support**: `MultiRBTree` class stores multiple values per key, with access to first or last value individually.
|
|
12
|
+
- **Pure Ruby**: No C extensions required. Works on MRI, JRuby, TruffleRuby, and all Ruby implementations.
|
|
13
|
+
- **Hybrid Indexing**: Internal hash index enables O(1) key lookup and membership checks — matching standard Hash performance.
|
|
14
|
+
- **Memory Efficiency**: Node recycling with auto-shrinking pool (`AutoShrinkNodePool`) drastically reduces GC pressure in long-running apps.
|
|
15
|
+
- **Nearest Key Search**: Finds the closest numeric key in O(log n) time — ideal for spatial or temporal queries.
|
|
16
|
+
- **Safe Iteration**: Use `safe: true` to safely modify the tree (insert/delete) during iteration.
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
16
19
|
|
|
@@ -77,9 +80,16 @@ tree.max # => [20, "twenty"]
|
|
|
77
80
|
|
|
78
81
|
# Range queries (return Enumerator, use .to_a for Array)
|
|
79
82
|
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
80
|
-
tree.gte(10).
|
|
83
|
+
tree.gte(10).each { |k, v| puts k } # Block iteration
|
|
81
84
|
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
82
85
|
|
|
86
|
+
# Range objects in [] (v0.3.4+)
|
|
87
|
+
tree[..10].to_a # lte(10)
|
|
88
|
+
tree[2..10].each { |k, v| ... } # Block iteration on Range
|
|
89
|
+
tree[2...10].to_a # between(2, 10, include_max: false)
|
|
90
|
+
tree[10..].to_a # gte(10)
|
|
91
|
+
tree[2..10, reverse: true].to_a # with options
|
|
92
|
+
|
|
83
93
|
# Shift and pop
|
|
84
94
|
tree.shift # => [1, "one"] (removes minimum)
|
|
85
95
|
tree.pop # => [20, "twenty"] (removes maximum)
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -191,11 +191,35 @@ class RBTree
|
|
|
191
191
|
# @example
|
|
192
192
|
# tree = RBTree.new({1 => 'one', 2 => 'two'})
|
|
193
193
|
# tree.get(1) # => "one"
|
|
194
|
-
# tree[2] # => "two"
|
|
195
|
-
# tree[3] # => nil
|
|
196
194
|
def value(key) = @hash_index[key]&.value
|
|
197
195
|
alias :get :value
|
|
198
|
-
|
|
196
|
+
|
|
197
|
+
# Retrieves a value associated with the given key, or a range of entries if a Range is provided.
|
|
198
|
+
#
|
|
199
|
+
# @param key_or_range [Object, Range] the key to look up or a Range for query
|
|
200
|
+
# @param ... [Hash] additional options to pass to the respective lookup method
|
|
201
|
+
# @return [Object, Enumerator, nil]
|
|
202
|
+
# - If a key is provided: the associated value, or nil if not found
|
|
203
|
+
# - If a Range is provided: an Enumerator yielding [key, value] pairs
|
|
204
|
+
# @example Single key lookup
|
|
205
|
+
# tree[2] # => "two"
|
|
206
|
+
# @example Range lookup
|
|
207
|
+
# tree[2..4].to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
208
|
+
# tree[...3].to_a # => [[1, "one"], [2, "two"]]
|
|
209
|
+
def [](key_or_range, **)
|
|
210
|
+
return value(key_or_range, **) if !key_or_range.is_a?(Range)
|
|
211
|
+
|
|
212
|
+
r = key_or_range
|
|
213
|
+
r.begin ? (
|
|
214
|
+
r.end ?
|
|
215
|
+
between(r.begin, r.end, include_max: !r.exclude_end?, **) :
|
|
216
|
+
gte(r.begin, **)
|
|
217
|
+
) : (
|
|
218
|
+
r.end ?
|
|
219
|
+
(r.exclude_end? ? lt(r.end, **) : lte(r.end, **)) :
|
|
220
|
+
each(**)
|
|
221
|
+
)
|
|
222
|
+
end
|
|
199
223
|
|
|
200
224
|
# Returns the key with the key closest to the given key.
|
|
201
225
|
#
|
|
@@ -432,7 +456,7 @@ class RBTree
|
|
|
432
456
|
# tree.delete(k) if k.even?
|
|
433
457
|
# end
|
|
434
458
|
def keys(reverse: false, safe: false, &block)
|
|
435
|
-
return enum_for(
|
|
459
|
+
return enum_for(__method__, reverse: reverse, safe: safe) { @key_count } unless block_given?
|
|
436
460
|
each(reverse: reverse, safe: safe) { |key, _| yield key }
|
|
437
461
|
self
|
|
438
462
|
end
|
|
@@ -459,7 +483,7 @@ class RBTree
|
|
|
459
483
|
# tree.delete(k) if k.even?
|
|
460
484
|
# end
|
|
461
485
|
def each(reverse: false, safe: false, &block)
|
|
462
|
-
return enum_for(
|
|
486
|
+
return enum_for(__method__, reverse: reverse, safe: safe) { size } unless block_given?
|
|
463
487
|
if reverse
|
|
464
488
|
traverse_all_desc(@root, safe: safe, &block)
|
|
465
489
|
else
|
|
@@ -488,7 +512,7 @@ class RBTree
|
|
|
488
512
|
# @return [Enumerator, RBTree] an Enumerator if no block is given, self otherwise
|
|
489
513
|
# @see #each
|
|
490
514
|
def reverse_each(safe: false, &block)
|
|
491
|
-
return enum_for(
|
|
515
|
+
return enum_for(__method__, safe: safe) { size } unless block_given?
|
|
492
516
|
each(reverse: true, safe: safe, &block)
|
|
493
517
|
end
|
|
494
518
|
|
|
@@ -505,7 +529,7 @@ class RBTree
|
|
|
505
529
|
# tree.lt(3, reverse: true).first # => [2, "two"]
|
|
506
530
|
# tree.lt(3, safe: true) { |k, _| tree.delete(k) if k.even? } # safe to delete
|
|
507
531
|
def lt(key, reverse: false, safe: false, &block)
|
|
508
|
-
return enum_for(
|
|
532
|
+
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
509
533
|
if reverse
|
|
510
534
|
traverse_lt_desc(@root, key, safe: safe, &block)
|
|
511
535
|
else
|
|
@@ -526,7 +550,7 @@ class RBTree
|
|
|
526
550
|
# tree.lte(3).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
527
551
|
# tree.lte(3, reverse: true).first # => [3, "three"]
|
|
528
552
|
def lte(key, reverse: false, safe: false, &block)
|
|
529
|
-
return enum_for(
|
|
553
|
+
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
530
554
|
if reverse
|
|
531
555
|
traverse_lte_desc(@root, key, safe: safe, &block)
|
|
532
556
|
else
|
|
@@ -547,7 +571,7 @@ class RBTree
|
|
|
547
571
|
# tree.gt(2).to_a # => [[3, "three"], [4, "four"]]
|
|
548
572
|
# tree.gt(2, reverse: true).first # => [4, "four"]
|
|
549
573
|
def gt(key, reverse: false, safe: false, &block)
|
|
550
|
-
return enum_for(
|
|
574
|
+
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
551
575
|
if reverse
|
|
552
576
|
traverse_gt_desc(@root, key, safe: safe, &block)
|
|
553
577
|
else
|
|
@@ -568,7 +592,7 @@ class RBTree
|
|
|
568
592
|
# tree.gte(2).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
569
593
|
# tree.gte(2, reverse: true).first # => [4, "four"]
|
|
570
594
|
def gte(key, reverse: false, safe: false, &block)
|
|
571
|
-
return enum_for(
|
|
595
|
+
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
572
596
|
if reverse
|
|
573
597
|
traverse_gte_desc(@root, key, safe: safe, &block)
|
|
574
598
|
else
|
|
@@ -592,7 +616,7 @@ class RBTree
|
|
|
592
616
|
# tree.between(2, 4).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
593
617
|
# tree.between(2, 4, reverse: true).first # => [4, "four"]
|
|
594
618
|
def between(min, max, include_min: true, include_max: true, reverse: false, safe: false, &block)
|
|
595
|
-
return enum_for(
|
|
619
|
+
return enum_for(__method__, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
|
|
596
620
|
if reverse
|
|
597
621
|
traverse_between_desc(@root, min, max, include_min, include_max, safe: safe, &block)
|
|
598
622
|
else
|
|
@@ -1194,6 +1218,8 @@ class RBTree
|
|
|
1194
1218
|
# @param key [Object] the reference key
|
|
1195
1219
|
# @return [Node] the successor node, or @nil_node if none exists
|
|
1196
1220
|
def find_successor_node(key)
|
|
1221
|
+
# If key is smaller than min_key, return min_node
|
|
1222
|
+
return @min_node if min_key && key < min_key
|
|
1197
1223
|
# Check if key exists using O(1) hash lookup
|
|
1198
1224
|
if (node = @hash_index[key])
|
|
1199
1225
|
# Key exists: find successor in subtree or ancestors
|
|
@@ -1496,6 +1522,7 @@ class MultiRBTree < RBTree
|
|
|
1496
1522
|
# tree.get(1) # => "first"
|
|
1497
1523
|
# tree.get(1, last: true) # => "second"
|
|
1498
1524
|
def value(key, last: false) = @hash_index[key]&.value&.send(last ? :last : :first)
|
|
1525
|
+
alias :get :value
|
|
1499
1526
|
|
|
1500
1527
|
# Retrieves the first value associated with the given key.
|
|
1501
1528
|
#
|
|
@@ -1521,7 +1548,7 @@ class MultiRBTree < RBTree
|
|
|
1521
1548
|
# tree.insert(1, 'second')
|
|
1522
1549
|
# tree.values(1).to_a # => ["first", "second"]
|
|
1523
1550
|
def values(key, reverse: false)
|
|
1524
|
-
return enum_for(
|
|
1551
|
+
return enum_for(__method__, key) { value_count(key) } unless block_given?
|
|
1525
1552
|
@hash_index[key]&.value&.send(reverse ? :reverse_each : :each) { |v| yield v }
|
|
1526
1553
|
end
|
|
1527
1554
|
alias :get_all :values
|
|
@@ -1622,6 +1649,7 @@ class MultiRBTree < RBTree
|
|
|
1622
1649
|
delete_indexed_node(z.key)
|
|
1623
1650
|
value
|
|
1624
1651
|
end
|
|
1652
|
+
alias :delete :delete_key
|
|
1625
1653
|
|
|
1626
1654
|
# Removes and returns the first key-value pair.
|
|
1627
1655
|
#
|