rbtree-ruby 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -4
- data/README.ja.md +275 -0
- data/README.md +12 -8
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +44 -88
- data/rbtree-ruby.gemspec +5 -4
- metadata +8 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea4c407bb808b5f0ce61e05d95bdb1b9a7efce1a6db4ef6c5688016d39eb66a1
|
|
4
|
+
data.tar.gz: 1dec0818d2844494de2daf36cca3b018d28589316b580d71879756e34ca3adb0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 654691a199723dd0b7426fea9165b372f4c71bcdadc8a58a90b76352b8eaade41145270ff6f88ffb37a4a22732a46b636d21515a07d4ae18b061c5d6fb154d53
|
|
7
|
+
data.tar.gz: 8e619ef4f92f440e92350748bfaadc80dc65f8a1daed06d6df1b73e20c4228ee943677496f56e9a8e413cb7081b8d98e6c7711a0b6ffe50fcbf8ab00bb65ed5a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.2.0] - 2026-01-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **MultiRBTree**: `nearest(key, last: false)` option to choose first or last value
|
|
12
|
+
|
|
13
|
+
## [0.1.8] - 2026-01-14
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Range Query Returns Enumerator**: `lt`, `lte`, `gt`, `gte`, `between` now return an `Enumerator` instead of an `Array` when no block is given
|
|
17
|
+
- Enables lazy evaluation: `tree.gt(100).lazy.take(5).to_a`
|
|
18
|
+
- Enables chaining: `tree.lt(50).map { |k, v| k }`
|
|
19
|
+
- Use `.to_a` to get an Array: `tree.lt(50).to_a`
|
|
20
|
+
- **Breaking**: Code using direct array access like `tree.lt(50)[0]` must change to `tree.lt(50).first`
|
|
21
|
+
|
|
8
22
|
## [0.1.7] - 2026-01-14
|
|
9
23
|
|
|
10
24
|
### Added
|
|
@@ -107,9 +121,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
107
121
|
- ASCII diagrams for tree rotation operations
|
|
108
122
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
109
123
|
|
|
124
|
+
[0.2.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.2.0
|
|
125
|
+
[0.1.8]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.8
|
|
110
126
|
[0.1.7]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.7
|
|
111
127
|
[0.1.6]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.6
|
|
112
|
-
[0.1.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.5
|
|
113
|
-
[0.1.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.4
|
|
114
|
-
[0.1.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.3
|
|
115
|
-
[0.1.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.2
|
data/README.ja.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# rbtree-ruby
|
|
2
|
+
|
|
3
|
+
🌍 *[English](README.md) | [日本語](README.ja.md)*
|
|
4
|
+
|
|
5
|
+
Red-Black Tree(赤黒木)データ構造のピュアRuby実装です。挿入、削除、検索操作がO(log n)の時間計算量で実行できる、効率的な順序付きキーバリューストレージを提供します。
|
|
6
|
+
|
|
7
|
+
## 特徴
|
|
8
|
+
|
|
9
|
+
- **自己平衡二分探索木**: 赤黒木の性質により最適なパフォーマンスを維持
|
|
10
|
+
- **順序付き操作**: 効率的な範囲クエリ、最小/最大値の取得、ソート済みイテレーション
|
|
11
|
+
- **複数値サポート**: `MultiRBTree`クラスで同一キーに複数の値を格納可能
|
|
12
|
+
- **ピュアRuby**: C拡張不要、あらゆるRuby実装で動作
|
|
13
|
+
- **充実したドキュメント**: 使用例付きの包括的なRDocドキュメント
|
|
14
|
+
|
|
15
|
+
## インストール
|
|
16
|
+
|
|
17
|
+
Gemfileに以下を追加:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'rbtree-ruby'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
実行:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bundle install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
または直接インストール:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gem install rbtree-ruby
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 使い方
|
|
36
|
+
|
|
37
|
+
### 基本的なRBTree
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
require 'rbtree'
|
|
41
|
+
|
|
42
|
+
# 空のツリーを作成
|
|
43
|
+
tree = RBTree.new
|
|
44
|
+
|
|
45
|
+
# データで初期化
|
|
46
|
+
tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
47
|
+
tree = RBTree[[5, 'five'], [4, 'four']]
|
|
48
|
+
|
|
49
|
+
# 値の挿入と取得
|
|
50
|
+
tree.insert(10, 'ten')
|
|
51
|
+
tree[20] = 'twenty'
|
|
52
|
+
puts tree[10] # => "ten"
|
|
53
|
+
|
|
54
|
+
# ソート順でイテレーション
|
|
55
|
+
tree.each { |key, value| puts "#{key}: #{value}" }
|
|
56
|
+
# 出力:
|
|
57
|
+
# 1: one
|
|
58
|
+
# 2: two
|
|
59
|
+
# 3: three
|
|
60
|
+
# 10: ten
|
|
61
|
+
# 20: twenty
|
|
62
|
+
|
|
63
|
+
# 最小値と最大値
|
|
64
|
+
tree.min # => [1, "one"]
|
|
65
|
+
tree.max # => [20, "twenty"]
|
|
66
|
+
|
|
67
|
+
# 範囲クエリ(Enumeratorを返す、配列には.to_aを使用)
|
|
68
|
+
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
69
|
+
tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
|
|
70
|
+
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
71
|
+
|
|
72
|
+
# shiftとpop
|
|
73
|
+
tree.shift # => [1, "one"] (最小値を削除)
|
|
74
|
+
tree.pop # => [20, "twenty"] (最大値を削除)
|
|
75
|
+
|
|
76
|
+
# 削除
|
|
77
|
+
tree.delete(3) # => "three"
|
|
78
|
+
|
|
79
|
+
# キーの存在確認
|
|
80
|
+
tree.has_key?(2) # => true
|
|
81
|
+
tree.size # => 2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### MultiRBTree(重複キー対応)
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
require 'rbtree'
|
|
88
|
+
|
|
89
|
+
tree = MultiRBTree.new
|
|
90
|
+
|
|
91
|
+
# 同じキーに複数の値を挿入
|
|
92
|
+
tree.insert(1, 'first one')
|
|
93
|
+
tree.insert(1, 'second one')
|
|
94
|
+
tree.insert(1, 'third one')
|
|
95
|
+
tree.insert(2, 'two')
|
|
96
|
+
|
|
97
|
+
tree.size # => 4 (キーバリューペアの総数)
|
|
98
|
+
|
|
99
|
+
# 最初の値を取得
|
|
100
|
+
tree.get(1) # => "first one"
|
|
101
|
+
tree[1] # => "first one"
|
|
102
|
+
|
|
103
|
+
# キーの全ての値を取得
|
|
104
|
+
tree.get_all(1) # => ["first one", "second one", "third one"]
|
|
105
|
+
|
|
106
|
+
# 全キーバリューペアをイテレーション
|
|
107
|
+
tree.each { |k, v| puts "#{k}: #{v}" }
|
|
108
|
+
# 出力:
|
|
109
|
+
# 1: first one
|
|
110
|
+
# 1: second one
|
|
111
|
+
# 1: third one
|
|
112
|
+
# 2: two
|
|
113
|
+
|
|
114
|
+
# 最初の値のみ削除
|
|
115
|
+
tree.delete_one(1) # => "first one"
|
|
116
|
+
tree.get(1) # => "second one"
|
|
117
|
+
|
|
118
|
+
# キーの全ての値を削除
|
|
119
|
+
tree.delete(1) # 残りの値を全て削除
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 最近傍キー検索
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
tree = RBTree.new({1 => 'one', 5 => 'five', 10 => 'ten'})
|
|
126
|
+
|
|
127
|
+
tree.nearest(4) # => [5, "five"] (4に最も近いキー)
|
|
128
|
+
tree.nearest(7) # => [5, "five"] (同距離の場合は小さいキー)
|
|
129
|
+
tree.nearest(8) # => [10, "ten"]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 前後キー検索
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
|
|
136
|
+
|
|
137
|
+
tree.prev(5) # => [3, "three"] (5より小さい最大のキー)
|
|
138
|
+
tree.succ(5) # => [7, "seven"] (5より大きい最小のキー)
|
|
139
|
+
|
|
140
|
+
# キーが存在しなくても動作
|
|
141
|
+
tree.prev(4) # => [3, "three"] (4は存在しない、4未満の最大キーを返す)
|
|
142
|
+
tree.succ(4) # => [5, "five"] (4は存在しない、4より大きい最小キーを返す)
|
|
143
|
+
|
|
144
|
+
# 境界ではnilを返す
|
|
145
|
+
tree.prev(1) # => nil (1より小さいキーなし)
|
|
146
|
+
tree.succ(7) # => nil (7より大きいキーなし)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 逆順範囲クエリ
|
|
150
|
+
|
|
151
|
+
範囲クエリは`Enumerator`を返し(配列には`.to_a`を使用)、`:reverse`オプションをサポート:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
155
|
+
|
|
156
|
+
tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
157
|
+
tree.lt(3, reverse: true).to_a # => [[2, "two"], [1, "one"]]
|
|
158
|
+
tree.lt(3).first # => [1, "one"] (遅延評価、配列は作成されない)
|
|
159
|
+
|
|
160
|
+
# 遅延評価
|
|
161
|
+
tree.gt(0).lazy.take(2).to_a # => [[1, "one"], [2, "two"]] (最初の2件のみ計算)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### MultiRBTree 値配列アクセス
|
|
165
|
+
|
|
166
|
+
複数の値を持つキーで、どの値にアクセスするか選択:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
tree = MultiRBTree.new
|
|
170
|
+
tree.insert(1, 'first')
|
|
171
|
+
tree.insert(1, 'second')
|
|
172
|
+
tree.insert(1, 'third')
|
|
173
|
+
|
|
174
|
+
# 最初または最後の値にアクセス
|
|
175
|
+
tree.get(1) # => "first"
|
|
176
|
+
tree.get(1, last: true) # => "third"
|
|
177
|
+
tree.get_first(1) # => "first"
|
|
178
|
+
tree.get_last(1) # => "third"
|
|
179
|
+
|
|
180
|
+
# どちらの端からも削除可能
|
|
181
|
+
tree.delete_first(1) # => "first"
|
|
182
|
+
tree.delete_last(1) # => "third"
|
|
183
|
+
tree.get(1) # => "second"
|
|
184
|
+
|
|
185
|
+
# min/maxの:lastオプション
|
|
186
|
+
tree.insert(2, 'a')
|
|
187
|
+
tree.insert(2, 'b')
|
|
188
|
+
tree.min # => [1, "second"] (最小キーの最初の値)
|
|
189
|
+
tree.max(last: true) # => [2, "b"] (最大キーの最後の値)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## パフォーマンス
|
|
193
|
+
|
|
194
|
+
主要な操作は**O(log n)**時間で実行:
|
|
195
|
+
|
|
196
|
+
- `insert(key, value)` - O(log n)
|
|
197
|
+
- `delete(key)` - O(log n)
|
|
198
|
+
- `get(key)` / `[]` - **O(1)** (ハイブリッドハッシュインデックス)
|
|
199
|
+
- `has_key?` - **O(1)** (ハイブリッドハッシュインデックス)
|
|
200
|
+
- `min` - **O(1)**
|
|
201
|
+
- `max` - O(log n)
|
|
202
|
+
- `shift` / `pop` - O(log n)
|
|
203
|
+
- `prev` / `succ` - O(log n)、O(1)ハッシュチェック付き
|
|
204
|
+
|
|
205
|
+
全要素のイテレーションはO(n)時間。
|
|
206
|
+
|
|
207
|
+
### メモリ効率
|
|
208
|
+
|
|
209
|
+
RBTreeは内部的な**メモリプール**を使用してノードオブジェクトを再利用:
|
|
210
|
+
- 頻繁な挿入・削除時のGC負荷を大幅に削減
|
|
211
|
+
- 100,000回の循環操作ベンチマークで**GC時間0.0秒**を達成
|
|
212
|
+
|
|
213
|
+
### RBTree vs Hash vs Array
|
|
214
|
+
|
|
215
|
+
順序付き操作と空間的操作において、RBTreeは単に速いだけでなく、全く異なるクラスの性能を発揮。**50万件**でのベンチマーク:
|
|
216
|
+
|
|
217
|
+
| 操作 | RBTree | Hash/Array | 高速化 | 理由 |
|
|
218
|
+
|-----|--------|------------|-------|-----|
|
|
219
|
+
| **最近傍検索** | **O(log n)** | O(n) スキャン | **〜8,600倍高速** | 二分探索 vs 全件スキャン |
|
|
220
|
+
| **範囲クエリ** | **O(log n + k)** | O(n) フィルター | **〜540倍高速** | 部分木へ直接ジャンプ vs 全件スキャン |
|
|
221
|
+
| **最小値抽出** | **O(log n)** | O(n) 検索 | **〜160倍高速** | 連続的なリバランス vs 全件スキャン |
|
|
222
|
+
| **ソート済みイテレーション** | **O(n)** | O(n log n) | **無料** | 常にソート済み vs 明示的な`sort` |
|
|
223
|
+
| **キー検索** | **O(1)** | O(1) | **同等** | ハイブリッドハッシュインデックス |
|
|
224
|
+
|
|
225
|
+
### RBTreeを使うべき場面
|
|
226
|
+
|
|
227
|
+
✅ **RBTreeが適している場合:**
|
|
228
|
+
- キー順でのイテレーション
|
|
229
|
+
- 高速なmin/max取得
|
|
230
|
+
- 範囲クエリ(`between`, `lt`, `gt`, `lte`, `gte`)
|
|
231
|
+
- 最近傍キー検索
|
|
232
|
+
- 優先度キュー的な動作(キー順でshift/pop)
|
|
233
|
+
|
|
234
|
+
✅ **Hashが適している場合:**
|
|
235
|
+
- 高速なキーバリュー検索のみ(RBTreeも同等に高速!)
|
|
236
|
+
- 順序付けが不要
|
|
237
|
+
|
|
238
|
+
## APIドキュメント
|
|
239
|
+
|
|
240
|
+
完全なRDocドキュメントを生成:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
rdoc lib/rbtree.rb
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`doc/index.html`をブラウザで開いてください。
|
|
247
|
+
|
|
248
|
+
## 開発
|
|
249
|
+
|
|
250
|
+
リポジトリをチェックアウト後、`bundle install`で依存関係をインストール:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# RDocドキュメント生成
|
|
254
|
+
rake rdoc
|
|
255
|
+
|
|
256
|
+
# gemのビルド
|
|
257
|
+
rake build
|
|
258
|
+
|
|
259
|
+
# ローカルインストール
|
|
260
|
+
rake install
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## コントリビューション
|
|
264
|
+
|
|
265
|
+
バグ報告やプルリクエストはGitHubで受け付けています: https://github.com/firelzrd/rbtree-ruby
|
|
266
|
+
|
|
267
|
+
## ライセンス
|
|
268
|
+
|
|
269
|
+
[MIT License](https://opensource.org/licenses/MIT)でオープンソースとして公開。
|
|
270
|
+
|
|
271
|
+
## 作者
|
|
272
|
+
|
|
273
|
+
Masahito Suzuki (firelzrd@gmail.com)
|
|
274
|
+
|
|
275
|
+
Copyright © 2026 Masahito Suzuki
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# rbtree-ruby
|
|
2
2
|
|
|
3
|
+
🌍 *[English](README.md) | [日本語](README.ja.md)*
|
|
4
|
+
|
|
3
5
|
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
6
|
|
|
5
7
|
## Features
|
|
@@ -62,10 +64,10 @@ tree.each { |key, value| puts "#{key}: #{value}" }
|
|
|
62
64
|
tree.min # => [1, "one"]
|
|
63
65
|
tree.max # => [20, "twenty"]
|
|
64
66
|
|
|
65
|
-
# Range queries
|
|
66
|
-
tree.lt(10) # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
67
|
-
tree.gte(10) # => [[10, "ten"], [20, "twenty"]]
|
|
68
|
-
tree.between(2, 10) # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
67
|
+
# Range queries (return Enumerator, use .to_a for Array)
|
|
68
|
+
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
69
|
+
tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
|
|
70
|
+
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
69
71
|
|
|
70
72
|
# Shift and pop
|
|
71
73
|
tree.shift # => [1, "one"] (removes minimum)
|
|
@@ -148,15 +150,17 @@ tree.succ(7) # => nil (no key larger than 7)
|
|
|
148
150
|
|
|
149
151
|
### Reverse Range Queries
|
|
150
152
|
|
|
151
|
-
All range queries support a `:reverse` option
|
|
153
|
+
All range queries return an `Enumerator` (use `.to_a` for Array) and support a `:reverse` option:
|
|
152
154
|
|
|
153
155
|
```ruby
|
|
154
156
|
tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
155
157
|
|
|
156
|
-
tree.lt(3) # => [[1, "one"], [2, "two"]]
|
|
157
|
-
tree.lt(3, reverse: true) # => [[2, "two"], [1, "one"]]
|
|
158
|
+
tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
159
|
+
tree.lt(3, reverse: true).to_a # => [[2, "two"], [1, "one"]]
|
|
160
|
+
tree.lt(3).first # => [1, "one"] (lazy, no array created)
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
# Lazy evaluation
|
|
163
|
+
tree.gt(0).lazy.take(2).to_a # => [[1, "one"], [2, "two"]] (only computes first 2)
|
|
160
164
|
```
|
|
161
165
|
|
|
162
166
|
### MultiRBTree Value Array Access
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -331,28 +331,20 @@ class RBTree
|
|
|
331
331
|
# @param key [Object] the upper bound (exclusive)
|
|
332
332
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
333
333
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
334
|
-
# @return [
|
|
334
|
+
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
335
335
|
# @example
|
|
336
336
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
337
|
-
# tree.lt(3) # => [[1, "one"], [2, "two"]]
|
|
338
|
-
# tree.lt(3, reverse: true) # => [
|
|
337
|
+
# tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
338
|
+
# tree.lt(3, reverse: true).first # => [2, "two"]
|
|
339
|
+
# tree.lt(3) { |k, v| puts k } # prints keys, returns self
|
|
339
340
|
def lt(key, reverse: false, &block)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
else
|
|
344
|
-
traverse_lt(@root, key, &block)
|
|
345
|
-
end
|
|
346
|
-
self
|
|
341
|
+
return enum_for(:lt, key, reverse: reverse) unless block_given?
|
|
342
|
+
if reverse
|
|
343
|
+
traverse_lt_desc(@root, key, &block)
|
|
347
344
|
else
|
|
348
|
-
|
|
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
|
|
354
|
-
res
|
|
345
|
+
traverse_lt(@root, key, &block)
|
|
355
346
|
end
|
|
347
|
+
self
|
|
356
348
|
end
|
|
357
349
|
|
|
358
350
|
# Retrieves all key-value pairs with keys less than or equal to the specified key.
|
|
@@ -360,28 +352,19 @@ class RBTree
|
|
|
360
352
|
# @param key [Object] the upper bound (inclusive)
|
|
361
353
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
362
354
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
363
|
-
# @return [
|
|
355
|
+
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
364
356
|
# @example
|
|
365
357
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
366
|
-
# tree.lte(3) # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
367
|
-
# tree.lte(3, reverse: true) # => [
|
|
358
|
+
# tree.lte(3).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
359
|
+
# tree.lte(3, reverse: true).first # => [3, "three"]
|
|
368
360
|
def lte(key, reverse: false, &block)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
else
|
|
373
|
-
traverse_lte(@root, key, &block)
|
|
374
|
-
end
|
|
375
|
-
self
|
|
361
|
+
return enum_for(:lte, key, reverse: reverse) unless block_given?
|
|
362
|
+
if reverse
|
|
363
|
+
traverse_lte_desc(@root, key, &block)
|
|
376
364
|
else
|
|
377
|
-
|
|
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
|
|
383
|
-
res
|
|
365
|
+
traverse_lte(@root, key, &block)
|
|
384
366
|
end
|
|
367
|
+
self
|
|
385
368
|
end
|
|
386
369
|
|
|
387
370
|
# Retrieves all key-value pairs with keys greater than the specified key.
|
|
@@ -389,28 +372,19 @@ class RBTree
|
|
|
389
372
|
# @param key [Object] the lower bound (exclusive)
|
|
390
373
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
391
374
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
392
|
-
# @return [
|
|
375
|
+
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
393
376
|
# @example
|
|
394
377
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
395
|
-
# tree.gt(2) # => [[3, "three"], [4, "four"]]
|
|
396
|
-
# tree.gt(2, reverse: true) # => [
|
|
378
|
+
# tree.gt(2).to_a # => [[3, "three"], [4, "four"]]
|
|
379
|
+
# tree.gt(2, reverse: true).first # => [4, "four"]
|
|
397
380
|
def gt(key, reverse: false, &block)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
else
|
|
402
|
-
traverse_gt(@root, key, &block)
|
|
403
|
-
end
|
|
404
|
-
self
|
|
381
|
+
return enum_for(:gt, key, reverse: reverse) unless block_given?
|
|
382
|
+
if reverse
|
|
383
|
+
traverse_gt_desc(@root, key, &block)
|
|
405
384
|
else
|
|
406
|
-
|
|
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
|
|
412
|
-
res
|
|
385
|
+
traverse_gt(@root, key, &block)
|
|
413
386
|
end
|
|
387
|
+
self
|
|
414
388
|
end
|
|
415
389
|
|
|
416
390
|
# Retrieves all key-value pairs with keys greater than or equal to the specified key.
|
|
@@ -418,28 +392,19 @@ class RBTree
|
|
|
418
392
|
# @param key [Object] the lower bound (inclusive)
|
|
419
393
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
420
394
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
421
|
-
# @return [
|
|
395
|
+
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
422
396
|
# @example
|
|
423
397
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
424
|
-
# tree.gte(2) # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
425
|
-
# tree.gte(2, reverse: true) # => [
|
|
398
|
+
# tree.gte(2).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
399
|
+
# tree.gte(2, reverse: true).first # => [4, "four"]
|
|
426
400
|
def gte(key, reverse: false, &block)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
else
|
|
431
|
-
traverse_gte(@root, key, &block)
|
|
432
|
-
end
|
|
433
|
-
self
|
|
401
|
+
return enum_for(:gte, key, reverse: reverse) unless block_given?
|
|
402
|
+
if reverse
|
|
403
|
+
traverse_gte_desc(@root, key, &block)
|
|
434
404
|
else
|
|
435
|
-
|
|
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
|
|
441
|
-
res
|
|
405
|
+
traverse_gte(@root, key, &block)
|
|
442
406
|
end
|
|
407
|
+
self
|
|
443
408
|
end
|
|
444
409
|
|
|
445
410
|
# Retrieves all key-value pairs with keys within the specified range.
|
|
@@ -450,29 +415,19 @@ class RBTree
|
|
|
450
415
|
# @param include_max [Boolean] whether to include the upper bound (default: true)
|
|
451
416
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
452
417
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
453
|
-
# @return [
|
|
418
|
+
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
454
419
|
# @example
|
|
455
420
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'})
|
|
456
|
-
# tree.between(2, 4) # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
457
|
-
# tree.between(2, 4,
|
|
458
|
-
# tree.between(2, 4, reverse: true) # => [[4, "four"], [3, "three"], [2, "two"]]
|
|
421
|
+
# tree.between(2, 4).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
422
|
+
# tree.between(2, 4, reverse: true).first # => [4, "four"]
|
|
459
423
|
def between(min, max, include_min: true, include_max: true, reverse: false, &block)
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
else
|
|
464
|
-
traverse_between(@root, min, max, include_min, include_max, &block)
|
|
465
|
-
end
|
|
466
|
-
self
|
|
424
|
+
return enum_for(:between, min, max, include_min: include_min, include_max: include_max, reverse: reverse) unless block_given?
|
|
425
|
+
if reverse
|
|
426
|
+
traverse_between_desc(@root, min, max, include_min, include_max, &block)
|
|
467
427
|
else
|
|
468
|
-
|
|
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
|
|
474
|
-
res
|
|
428
|
+
traverse_between(@root, min, max, include_min, include_max, &block)
|
|
475
429
|
end
|
|
430
|
+
self
|
|
476
431
|
end
|
|
477
432
|
|
|
478
433
|
# Returns the key-value pair with the key closest to the given key.
|
|
@@ -1506,9 +1461,10 @@ class MultiRBTree < RBTree
|
|
|
1506
1461
|
[n.key, last ? n.value.last : n.value.first]
|
|
1507
1462
|
end
|
|
1508
1463
|
|
|
1509
|
-
def nearest(key)
|
|
1464
|
+
def nearest(key, last: false)
|
|
1510
1465
|
n = find_nearest_node(key)
|
|
1511
|
-
n == @nil_node || n.value.empty?
|
|
1466
|
+
return nil if n == @nil_node || n.value.empty?
|
|
1467
|
+
[n.key, last ? n.value.last : n.value.first]
|
|
1512
1468
|
end
|
|
1513
1469
|
|
|
1514
1470
|
def prev(key, last: false)
|
data/rbtree-ruby.gemspec
CHANGED
|
@@ -8,11 +8,12 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ["Masahito Suzuki"]
|
|
9
9
|
spec.email = ["firelzrd@gmail.com"]
|
|
10
10
|
|
|
11
|
-
spec.summary = "A pure Ruby
|
|
11
|
+
spec.summary = "A pure Ruby Red-Black Tree with O(1) lookup, range queries, and multi-value support"
|
|
12
12
|
spec.description = <<~DESC
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
A high-performance pure Ruby Red-Black Tree implementation. Features: O(1) key
|
|
14
|
+
lookup via hybrid hash index, O(log n) insert/delete, lazy Enumerator-based
|
|
15
|
+
range queries (lt/gt/between), nearest/prev/succ search, memory-efficient node
|
|
16
|
+
pooling, and MultiRBTree for duplicate keys with first/last value access.
|
|
16
17
|
DESC
|
|
17
18
|
spec.homepage = "https://github.com/firelzrd/rbtree-ruby"
|
|
18
19
|
spec.license = "MIT"
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masahito Suzuki
|
|
@@ -10,9 +10,10 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: |
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
A high-performance pure Ruby Red-Black Tree implementation. Features: O(1) key
|
|
14
|
+
lookup via hybrid hash index, O(log n) insert/delete, lazy Enumerator-based
|
|
15
|
+
range queries (lt/gt/between), nearest/prev/succ search, memory-efficient node
|
|
16
|
+
pooling, and MultiRBTree for duplicate keys with first/last value access.
|
|
16
17
|
email:
|
|
17
18
|
- firelzrd@gmail.com
|
|
18
19
|
executables: []
|
|
@@ -21,6 +22,7 @@ extra_rdoc_files: []
|
|
|
21
22
|
files:
|
|
22
23
|
- CHANGELOG.md
|
|
23
24
|
- LICENSE
|
|
25
|
+
- README.ja.md
|
|
24
26
|
- README.md
|
|
25
27
|
- Rakefile
|
|
26
28
|
- lib/rbtree.rb
|
|
@@ -49,5 +51,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
49
51
|
requirements: []
|
|
50
52
|
rubygems_version: 4.0.3
|
|
51
53
|
specification_version: 4
|
|
52
|
-
summary: A pure Ruby
|
|
54
|
+
summary: A pure Ruby Red-Black Tree with O(1) lookup, range queries, and multi-value
|
|
55
|
+
support
|
|
53
56
|
test_files: []
|