rbtree-ruby 0.1.8 → 0.2.1
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 +25 -0
- data/README.ja.md +282 -0
- data/README.md +22 -11
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +271 -59
- 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: 89348dfde7177409ecdca6a4184ebee7f83f3a57eebb84b3f4925ad4460a7a92
|
|
4
|
+
data.tar.gz: bd46b055e05a121787b67ecf20836181800abe0a230800703872c1412826ccfa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 908a2d3d58bfac4c7d092e745ebe7f3f2c391551ea5fa7822871d1d0334c64985169df7433342e0e77cad9cf85f8a6b1a265ee427d96445095b3aa27da73d8ef
|
|
7
|
+
data.tar.gz: c7b5afe4a5189deee48c6abc5cbca6340c14ebfc65d581f2788117bfb84aabca7757582ab5b6bbcf7cc5866e6350be4f85f87fda78ea49af8c394dd0884de6b3
|
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.2.1] - 2026-01-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Enhanced iteration**: `each` now accepts `reverse:` and `safe:` options
|
|
12
|
+
- `tree.each(reverse: true) { ... }` (same as `reverse_each`)
|
|
13
|
+
- `tree.each(safe: true) { ... }` (modification-safe)
|
|
14
|
+
- `tree.each(reverse: true, safe: true) { ... }` (reverse modification-safe)
|
|
15
|
+
- In `MultiRBTree`, `reverse: true` also iterates over values in reverse insertion order.
|
|
16
|
+
- **Range query safe mode**: `safe:` option for `lt`, `lte`, `gt`, `gte`, `between`
|
|
17
|
+
- `tree.lt(10, safe: true) { |k, _| tree.delete(k) if k.even? }`
|
|
18
|
+
- Works with `:reverse` option: `tree.gt(5, reverse: true, safe: true)`
|
|
19
|
+
- Returns Enumerator: `tree.between(1, 100, safe: true).select { ... }`
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **MultiRBTree#get_all**: Now returns an `Enumerator` instead of an `Array` when no block is given.
|
|
23
|
+
- Improves consistency with other iteration methods.
|
|
24
|
+
- Safe handling for non-existent keys (returns empty Enumerator/nil logic handled safely).
|
|
25
|
+
- Use `.to_a` to get an Array: `tree.get_all(key).to_a`
|
|
26
|
+
|
|
27
|
+
## [0.2.0] - 2026-01-14
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **MultiRBTree**: `nearest(key, last: false)` option to choose first or last value
|
|
31
|
+
|
|
8
32
|
## [0.1.8] - 2026-01-14
|
|
9
33
|
|
|
10
34
|
### Changed
|
|
@@ -116,6 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
116
140
|
- ASCII diagrams for tree rotation operations
|
|
117
141
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
118
142
|
|
|
143
|
+
[0.2.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.2.0
|
|
119
144
|
[0.1.8]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.8
|
|
120
145
|
[0.1.7]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.7
|
|
121
146
|
[0.1.6]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.6
|
data/README.ja.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
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
|
+
- **順序付き操作**: 効率的な範囲クエリ、最小/最大値の取得、ソート済みイテレーション
|
|
12
|
+
- **複数値サポート**: `MultiRBTree`クラスで同一キーに複数の値を格納可能
|
|
13
|
+
- **ピュアRuby**: C拡張不要、あらゆるRuby実装で動作
|
|
14
|
+
- **充実したドキュメント**: 使用例付きの包括的なRDocドキュメント
|
|
15
|
+
|
|
16
|
+
## インストール
|
|
17
|
+
|
|
18
|
+
Gemfileに以下を追加:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'rbtree-ruby'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
実行:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bundle install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
または直接インストール:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gem install rbtree-ruby
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 使い方
|
|
37
|
+
|
|
38
|
+
### 基本的なRBTree
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
require 'rbtree'
|
|
42
|
+
|
|
43
|
+
# 空のツリーを作成
|
|
44
|
+
tree = RBTree.new
|
|
45
|
+
|
|
46
|
+
# データで初期化
|
|
47
|
+
tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
48
|
+
tree = RBTree[[5, 'five'], [4, 'four']]
|
|
49
|
+
|
|
50
|
+
# 値の挿入と取得
|
|
51
|
+
tree.insert(10, 'ten')
|
|
52
|
+
tree[20] = 'twenty'
|
|
53
|
+
puts tree[10] # => "ten"
|
|
54
|
+
|
|
55
|
+
# ソート順でイテレーション
|
|
56
|
+
tree.each { |key, value| puts "#{key}: #{value}" }
|
|
57
|
+
# 出力:
|
|
58
|
+
# 1: one
|
|
59
|
+
# 2: two
|
|
60
|
+
# 3: three
|
|
61
|
+
# 10: ten
|
|
62
|
+
# 20: twenty
|
|
63
|
+
|
|
64
|
+
# イテレーション中の変更
|
|
65
|
+
# 標準のHashやArrayとは異なり、`safe: true`オプションを指定することで
|
|
66
|
+
# イテレーション中に安全にキーの削除や挿入を行うことができます。
|
|
67
|
+
tree.each(safe: true) { |k, v| tree.delete(k) if k.even? }
|
|
68
|
+
tree.each(reverse: true) { |k, v| puts k } # reverse_eachと同じ
|
|
69
|
+
|
|
70
|
+
# 最小値と最大値
|
|
71
|
+
tree.min # => [1, "one"]
|
|
72
|
+
tree.max # => [20, "twenty"]
|
|
73
|
+
|
|
74
|
+
# 範囲クエリ(Enumeratorを返す、配列には.to_aを使用)
|
|
75
|
+
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
76
|
+
tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
|
|
77
|
+
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
78
|
+
|
|
79
|
+
# shiftとpop
|
|
80
|
+
tree.shift # => [1, "one"] (最小値を削除)
|
|
81
|
+
tree.pop # => [20, "twenty"] (最大値を削除)
|
|
82
|
+
|
|
83
|
+
# 削除
|
|
84
|
+
tree.delete(3) # => "three"
|
|
85
|
+
|
|
86
|
+
# キーの存在確認
|
|
87
|
+
tree.has_key?(2) # => true
|
|
88
|
+
tree.size # => 2
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### MultiRBTree(重複キー対応)
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
require 'rbtree'
|
|
95
|
+
|
|
96
|
+
tree = MultiRBTree.new
|
|
97
|
+
|
|
98
|
+
# 同じキーに複数の値を挿入
|
|
99
|
+
tree.insert(1, 'first one')
|
|
100
|
+
tree.insert(1, 'second one')
|
|
101
|
+
tree.insert(1, 'third one')
|
|
102
|
+
tree.insert(2, 'two')
|
|
103
|
+
|
|
104
|
+
tree.size # => 4 (キーバリューペアの総数)
|
|
105
|
+
|
|
106
|
+
# 最初の値を取得
|
|
107
|
+
tree.get(1) # => "first one"
|
|
108
|
+
tree[1] # => "first one"
|
|
109
|
+
|
|
110
|
+
# キーの全ての値を取得(Enumeratorを返す)
|
|
111
|
+
tree.get_all(1).to_a # => ["first one", "second one", "third one"]
|
|
112
|
+
|
|
113
|
+
# 全キーバリューペアをイテレーション
|
|
114
|
+
tree.each { |k, v| puts "#{k}: #{v}" }
|
|
115
|
+
# 出力:
|
|
116
|
+
# 1: first one
|
|
117
|
+
# 1: second one
|
|
118
|
+
# 1: third one
|
|
119
|
+
# 2: two
|
|
120
|
+
|
|
121
|
+
# 最初の値のみ削除
|
|
122
|
+
tree.delete_one(1) # => "first one"
|
|
123
|
+
tree.get(1) # => "second one"
|
|
124
|
+
|
|
125
|
+
# キーの全ての値を削除
|
|
126
|
+
tree.delete(1) # 残りの値を全て削除
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 最近傍キー検索
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
tree = RBTree.new({1 => 'one', 5 => 'five', 10 => 'ten'})
|
|
133
|
+
|
|
134
|
+
tree.nearest(4) # => [5, "five"] (4に最も近いキー)
|
|
135
|
+
tree.nearest(7) # => [5, "five"] (同距離の場合は小さいキー)
|
|
136
|
+
tree.nearest(8) # => [10, "ten"]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 前後キー検索
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
|
|
143
|
+
|
|
144
|
+
tree.prev(5) # => [3, "three"] (5より小さい最大のキー)
|
|
145
|
+
tree.succ(5) # => [7, "seven"] (5より大きい最小のキー)
|
|
146
|
+
|
|
147
|
+
# キーが存在しなくても動作
|
|
148
|
+
tree.prev(4) # => [3, "three"] (4は存在しない、4未満の最大キーを返す)
|
|
149
|
+
tree.succ(4) # => [5, "five"] (4は存在しない、4より大きい最小キーを返す)
|
|
150
|
+
|
|
151
|
+
# 境界ではnilを返す
|
|
152
|
+
tree.prev(1) # => nil (1より小さいキーなし)
|
|
153
|
+
tree.succ(7) # => nil (7より大きいキーなし)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 逆順範囲クエリ
|
|
157
|
+
|
|
158
|
+
範囲クエリは`Enumerator`を返し(配列には`.to_a`を使用)、`:reverse`オプションをサポート:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
162
|
+
|
|
163
|
+
tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
164
|
+
tree.lt(3, reverse: true).to_a # => [[2, "two"], [1, "one"]]
|
|
165
|
+
tree.lt(3).first # => [1, "one"] (遅延評価、配列は作成されない)
|
|
166
|
+
|
|
167
|
+
# 遅延評価
|
|
168
|
+
tree.gt(0).lazy.take(2).to_a # => [[1, "one"], [2, "two"]] (最初の2件のみ計算)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### MultiRBTree 値配列アクセス
|
|
172
|
+
|
|
173
|
+
複数の値を持つキーで、どの値にアクセスするか選択:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
tree = MultiRBTree.new
|
|
177
|
+
tree.insert(1, 'first')
|
|
178
|
+
tree.insert(1, 'second')
|
|
179
|
+
tree.insert(1, 'third')
|
|
180
|
+
|
|
181
|
+
# 最初または最後の値にアクセス
|
|
182
|
+
tree.get(1) # => "first"
|
|
183
|
+
tree.get(1, last: true) # => "third"
|
|
184
|
+
tree.get_first(1) # => "first"
|
|
185
|
+
tree.get_last(1) # => "third"
|
|
186
|
+
|
|
187
|
+
# どちらの端からも削除可能
|
|
188
|
+
tree.delete_first(1) # => "first"
|
|
189
|
+
tree.delete_last(1) # => "third"
|
|
190
|
+
tree.get(1) # => "second"
|
|
191
|
+
|
|
192
|
+
# min/maxの:lastオプション
|
|
193
|
+
tree.insert(2, 'a')
|
|
194
|
+
tree.insert(2, 'b')
|
|
195
|
+
tree.min # => [1, "second"] (最小キーの最初の値)
|
|
196
|
+
tree.max(last: true) # => [2, "b"] (最大キーの最後の値)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## パフォーマンス
|
|
200
|
+
|
|
201
|
+
主要な操作は**O(log n)**時間で実行:
|
|
202
|
+
|
|
203
|
+
- `insert(key, value)` - O(log n)
|
|
204
|
+
- `delete(key)` - O(log n)
|
|
205
|
+
- `get(key)` / `[]` - **O(1)** (ハイブリッドハッシュインデックス)
|
|
206
|
+
- `has_key?` - **O(1)** (ハイブリッドハッシュインデックス)
|
|
207
|
+
- `min` - **O(1)**
|
|
208
|
+
- `max` - O(log n)
|
|
209
|
+
- `shift` / `pop` - O(log n)
|
|
210
|
+
- `prev` / `succ` - O(log n)、O(1)ハッシュチェック付き
|
|
211
|
+
|
|
212
|
+
全要素のイテレーションはO(n)時間。
|
|
213
|
+
|
|
214
|
+
### メモリ効率
|
|
215
|
+
|
|
216
|
+
RBTreeは内部的な**メモリプール**を使用してノードオブジェクトを再利用:
|
|
217
|
+
- 頻繁な挿入・削除時のGC負荷を大幅に削減
|
|
218
|
+
- 100,000回の循環操作ベンチマークで**GC時間0.0秒**を達成
|
|
219
|
+
|
|
220
|
+
### RBTree vs Hash vs Array
|
|
221
|
+
|
|
222
|
+
順序付き操作と空間的操作において、RBTreeは単に速いだけでなく、全く異なるクラスの性能を発揮。**50万件**でのベンチマーク:
|
|
223
|
+
|
|
224
|
+
| 操作 | RBTree | Hash/Array | 高速化 | 理由 |
|
|
225
|
+
|-----|--------|------------|-------|-----|
|
|
226
|
+
| **最近傍検索** | **O(log n)** | O(n) スキャン | **〜8,600倍高速** | 二分探索 vs 全件スキャン |
|
|
227
|
+
| **範囲クエリ** | **O(log n + k)** | O(n) フィルター | **〜540倍高速** | 部分木へ直接ジャンプ vs 全件スキャン |
|
|
228
|
+
| **最小値抽出** | **O(log n)** | O(n) 検索 | **〜160倍高速** | 連続的なリバランス vs 全件スキャン |
|
|
229
|
+
| **ソート済みイテレーション** | **O(n)** | O(n log n) | **無料** | 常にソート済み vs 明示的な`sort` |
|
|
230
|
+
| **キー検索** | **O(1)** | O(1) | **同等** | ハイブリッドハッシュインデックス |
|
|
231
|
+
|
|
232
|
+
### RBTreeを使うべき場面
|
|
233
|
+
|
|
234
|
+
✅ **RBTreeが適している場合:**
|
|
235
|
+
- キー順でのイテレーション
|
|
236
|
+
- 高速なmin/max取得
|
|
237
|
+
- 範囲クエリ(`between`, `lt`, `gt`, `lte`, `gte`)
|
|
238
|
+
- 最近傍キー検索
|
|
239
|
+
- 優先度キュー的な動作(キー順でshift/pop)
|
|
240
|
+
|
|
241
|
+
✅ **Hashが適している場合:**
|
|
242
|
+
- 高速なキーバリュー検索のみ(RBTreeも同等に高速!)
|
|
243
|
+
- 順序付けが不要
|
|
244
|
+
|
|
245
|
+
## APIドキュメント
|
|
246
|
+
|
|
247
|
+
完全なRDocドキュメントを生成:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
rdoc lib/rbtree.rb
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`doc/index.html`をブラウザで開いてください。
|
|
254
|
+
|
|
255
|
+
## 開発
|
|
256
|
+
|
|
257
|
+
リポジトリをチェックアウト後、`bundle install`で依存関係をインストール:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# RDocドキュメント生成
|
|
261
|
+
rake rdoc
|
|
262
|
+
|
|
263
|
+
# gemのビルド
|
|
264
|
+
rake build
|
|
265
|
+
|
|
266
|
+
# ローカルインストール
|
|
267
|
+
rake install
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## コントリビューション
|
|
271
|
+
|
|
272
|
+
バグ報告やプルリクエストはGitHubで受け付けています: https://github.com/firelzrd/rbtree-ruby
|
|
273
|
+
|
|
274
|
+
## ライセンス
|
|
275
|
+
|
|
276
|
+
[MIT License](https://opensource.org/licenses/MIT)でオープンソースとして公開。
|
|
277
|
+
|
|
278
|
+
## 作者
|
|
279
|
+
|
|
280
|
+
Masahito Suzuki (firelzrd@gmail.com)
|
|
281
|
+
|
|
282
|
+
Copyright © 2026 Masahito Suzuki
|
data/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# rbtree-ruby
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
🌍 *[English](README.md) | [日本語](README.ja.md)*
|
|
4
|
+
|
|
5
|
+
A pure Ruby implementation of the Red-Black Tree data structure, providing efficient ordered
|
|
6
|
+
key-value storage with O(log n) time complexity for insertion, deletion, and lookup operations.
|
|
4
7
|
|
|
5
8
|
## Features
|
|
6
9
|
|
|
@@ -58,14 +61,20 @@ tree.each { |key, value| puts "#{key}: #{value}" }
|
|
|
58
61
|
# 10: ten
|
|
59
62
|
# 20: twenty
|
|
60
63
|
|
|
64
|
+
# Modification during iteration
|
|
65
|
+
# Unlike standard Ruby Hash/Array, modification during iteration is fully supported
|
|
66
|
+
# with the `safe: true` option. This allows you to delete or insert keys safely while iterating.
|
|
67
|
+
tree.each(safe: true) { |k, v| tree.delete(k) if k.even? }
|
|
68
|
+
tree.each(reverse: true) { |k, v| puts k } # Same as reverse_each
|
|
69
|
+
|
|
61
70
|
# Min and max
|
|
62
71
|
tree.min # => [1, "one"]
|
|
63
72
|
tree.max # => [20, "twenty"]
|
|
64
73
|
|
|
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"]]
|
|
74
|
+
# Range queries (return Enumerator, use .to_a for Array)
|
|
75
|
+
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
76
|
+
tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
|
|
77
|
+
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
69
78
|
|
|
70
79
|
# Shift and pop
|
|
71
80
|
tree.shift # => [1, "one"] (removes minimum)
|
|
@@ -98,8 +107,8 @@ tree.size # => 4 (total number of key-value pairs)
|
|
|
98
107
|
tree.get(1) # => "first one"
|
|
99
108
|
tree[1] # => "first one"
|
|
100
109
|
|
|
101
|
-
# Get all values for a key
|
|
102
|
-
tree.get_all(1) # => ["first one", "second one", "third one"]
|
|
110
|
+
# Get all values for a key (returns Enumerator)
|
|
111
|
+
tree.get_all(1).to_a # => ["first one", "second one", "third one"]
|
|
103
112
|
|
|
104
113
|
# Iterate over all key-value pairs
|
|
105
114
|
tree.each { |k, v| puts "#{k}: #{v}" }
|
|
@@ -148,15 +157,17 @@ tree.succ(7) # => nil (no key larger than 7)
|
|
|
148
157
|
|
|
149
158
|
### Reverse Range Queries
|
|
150
159
|
|
|
151
|
-
All range queries support a `:reverse` option
|
|
160
|
+
All range queries return an `Enumerator` (use `.to_a` for Array) and support a `:reverse` option:
|
|
152
161
|
|
|
153
162
|
```ruby
|
|
154
163
|
tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
155
164
|
|
|
156
|
-
tree.lt(3) # => [[1, "one"], [2, "two"]]
|
|
157
|
-
tree.lt(3, reverse: true) # => [[2, "two"], [1, "one"]]
|
|
165
|
+
tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
166
|
+
tree.lt(3, reverse: true).to_a # => [[2, "two"], [1, "one"]]
|
|
167
|
+
tree.lt(3).first # => [1, "one"] (lazy, no array created)
|
|
158
168
|
|
|
159
|
-
|
|
169
|
+
# Lazy evaluation
|
|
170
|
+
tree.gt(0).lazy.take(2).to_a # => [[1, "one"], [2, "two"]] (only computes first 2)
|
|
160
171
|
```
|
|
161
172
|
|
|
162
173
|
### MultiRBTree Value Array Access
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -268,13 +268,15 @@ class RBTree
|
|
|
268
268
|
def pop
|
|
269
269
|
n = rightmost(@root)
|
|
270
270
|
return nil if n == @nil_node
|
|
271
|
-
result =
|
|
271
|
+
result = n.pair
|
|
272
272
|
delete(n.key)
|
|
273
273
|
result
|
|
274
274
|
end
|
|
275
275
|
|
|
276
|
-
# Iterates over all key-value pairs in ascending
|
|
276
|
+
# Iterates over all key-value pairs in ascending (or descending) order.
|
|
277
277
|
#
|
|
278
|
+
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
279
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
278
280
|
# @yield [key, value] each key-value pair in the tree
|
|
279
281
|
# @return [Enumerator, RBTree] an Enumerator if no block is given, self otherwise
|
|
280
282
|
# @example
|
|
@@ -284,9 +286,40 @@ class RBTree
|
|
|
284
286
|
# # 1: one
|
|
285
287
|
# # 2: two
|
|
286
288
|
# # 3: three
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
289
|
+
#
|
|
290
|
+
# # Reverse iteration
|
|
291
|
+
# tree.each(reverse: true) { |k, v| ... }
|
|
292
|
+
#
|
|
293
|
+
# # Safe iteration for modifications
|
|
294
|
+
# tree.each(safe: true) do |k, v|
|
|
295
|
+
# tree.delete(k) if k.even?
|
|
296
|
+
# end
|
|
297
|
+
def each(reverse: false, safe: false, &block)
|
|
298
|
+
return enum_for(:each, reverse: reverse, safe: safe) unless block_given?
|
|
299
|
+
if safe
|
|
300
|
+
if reverse
|
|
301
|
+
pair = find_max
|
|
302
|
+
while pair
|
|
303
|
+
current_key = pair[0]
|
|
304
|
+
yield pair[0], pair[1]
|
|
305
|
+
pair = find_predecessor(current_key)
|
|
306
|
+
end
|
|
307
|
+
else
|
|
308
|
+
pair = find_min
|
|
309
|
+
while pair
|
|
310
|
+
current_key = pair[0]
|
|
311
|
+
yield pair[0], pair[1]
|
|
312
|
+
pair = find_successor(current_key)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
else
|
|
316
|
+
if reverse
|
|
317
|
+
traverse_desc(@root, &block)
|
|
318
|
+
else
|
|
319
|
+
traverse_asc(@root, &block)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
self
|
|
290
323
|
end
|
|
291
324
|
|
|
292
325
|
# Iterates over all key-value pairs in descending order of keys.
|
|
@@ -300,10 +333,15 @@ class RBTree
|
|
|
300
333
|
# # 3: three
|
|
301
334
|
# # 2: two
|
|
302
335
|
# # 1: one
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
336
|
+
# Iterates over all key-value pairs in descending order of keys.
|
|
337
|
+
#
|
|
338
|
+
# This is an alias for `each(reverse: true)`.
|
|
339
|
+
#
|
|
340
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
341
|
+
# @yield [key, value] each key-value pair in the tree
|
|
342
|
+
# @return [Enumerator, RBTree] an Enumerator if no block is given, self otherwise
|
|
343
|
+
# @see #each
|
|
344
|
+
def reverse_each(safe: false, &block) = each(reverse: true, safe: safe, &block)
|
|
307
345
|
|
|
308
346
|
# Returns the minimum key-value pair without removing it.
|
|
309
347
|
#
|
|
@@ -311,9 +349,7 @@ class RBTree
|
|
|
311
349
|
# @example
|
|
312
350
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
313
351
|
# tree.min # => [1, "one"]
|
|
314
|
-
def min
|
|
315
|
-
@min_node == @nil_node ? nil : [@min_node.key, @min_node.value]
|
|
316
|
-
end
|
|
352
|
+
def min = find_min
|
|
317
353
|
|
|
318
354
|
# Returns the maximum key-value pair without removing it.
|
|
319
355
|
#
|
|
@@ -321,25 +357,39 @@ class RBTree
|
|
|
321
357
|
# @example
|
|
322
358
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
323
359
|
# tree.max # => [3, "three"]
|
|
324
|
-
def max
|
|
325
|
-
n = rightmost(@root)
|
|
326
|
-
n == @nil_node ? nil : [n.key, n.value]
|
|
327
|
-
end
|
|
360
|
+
def max = find_max
|
|
328
361
|
|
|
329
362
|
# Retrieves all key-value pairs with keys less than the specified key.
|
|
330
363
|
#
|
|
331
364
|
# @param key [Object] the upper bound (exclusive)
|
|
332
365
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
366
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
333
367
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
334
368
|
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
335
369
|
# @example
|
|
336
370
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
337
371
|
# tree.lt(3).to_a # => [[1, "one"], [2, "two"]]
|
|
338
372
|
# tree.lt(3, reverse: true).first # => [2, "two"]
|
|
339
|
-
# tree.lt(3) { |k,
|
|
340
|
-
def lt(key, reverse: false, &block)
|
|
341
|
-
return enum_for(:lt, key, reverse: reverse) unless block_given?
|
|
342
|
-
if
|
|
373
|
+
# tree.lt(3, safe: true) { |k, _| tree.delete(k) if k.even? } # safe to delete
|
|
374
|
+
def lt(key, reverse: false, safe: false, &block)
|
|
375
|
+
return enum_for(:lt, key, reverse: reverse, safe: safe) unless block_given?
|
|
376
|
+
if safe
|
|
377
|
+
if reverse
|
|
378
|
+
pair = find_predecessor(key)
|
|
379
|
+
while pair
|
|
380
|
+
current_key = pair[0]
|
|
381
|
+
yield pair
|
|
382
|
+
pair = find_predecessor(current_key)
|
|
383
|
+
end
|
|
384
|
+
else
|
|
385
|
+
pair = find_min
|
|
386
|
+
while pair && pair[0] < key
|
|
387
|
+
current_key = pair[0]
|
|
388
|
+
yield pair
|
|
389
|
+
pair = find_successor(current_key)
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
elsif reverse
|
|
343
393
|
traverse_lt_desc(@root, key, &block)
|
|
344
394
|
else
|
|
345
395
|
traverse_lt(@root, key, &block)
|
|
@@ -351,15 +401,32 @@ class RBTree
|
|
|
351
401
|
#
|
|
352
402
|
# @param key [Object] the upper bound (inclusive)
|
|
353
403
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
404
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
354
405
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
355
406
|
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
356
407
|
# @example
|
|
357
408
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
358
409
|
# tree.lte(3).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
359
410
|
# tree.lte(3, reverse: true).first # => [3, "three"]
|
|
360
|
-
def lte(key, reverse: false, &block)
|
|
361
|
-
return enum_for(:lte, key, reverse: reverse) unless block_given?
|
|
362
|
-
if
|
|
411
|
+
def lte(key, reverse: false, safe: false, &block)
|
|
412
|
+
return enum_for(:lte, key, reverse: reverse, safe: safe) unless block_given?
|
|
413
|
+
if safe
|
|
414
|
+
if reverse
|
|
415
|
+
pair = @hash_index[key]&.pair || find_predecessor(key)
|
|
416
|
+
while pair
|
|
417
|
+
current_key = pair[0]
|
|
418
|
+
yield pair
|
|
419
|
+
pair = find_predecessor(current_key)
|
|
420
|
+
end
|
|
421
|
+
else
|
|
422
|
+
pair = find_min
|
|
423
|
+
while pair && pair[0] <= key
|
|
424
|
+
current_key = pair[0]
|
|
425
|
+
yield pair
|
|
426
|
+
pair = succ(current_key)
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
elsif reverse
|
|
363
430
|
traverse_lte_desc(@root, key, &block)
|
|
364
431
|
else
|
|
365
432
|
traverse_lte(@root, key, &block)
|
|
@@ -371,15 +438,32 @@ class RBTree
|
|
|
371
438
|
#
|
|
372
439
|
# @param key [Object] the lower bound (exclusive)
|
|
373
440
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
441
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
374
442
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
375
443
|
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
376
444
|
# @example
|
|
377
445
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
378
446
|
# tree.gt(2).to_a # => [[3, "three"], [4, "four"]]
|
|
379
447
|
# tree.gt(2, reverse: true).first # => [4, "four"]
|
|
380
|
-
def gt(key, reverse: false, &block)
|
|
381
|
-
return enum_for(:gt, key, reverse: reverse) unless block_given?
|
|
382
|
-
if
|
|
448
|
+
def gt(key, reverse: false, safe: false, &block)
|
|
449
|
+
return enum_for(:gt, key, reverse: reverse, safe: safe) unless block_given?
|
|
450
|
+
if safe
|
|
451
|
+
if reverse
|
|
452
|
+
pair = find_max
|
|
453
|
+
while pair && pair[0] > key
|
|
454
|
+
current_key = pair[0]
|
|
455
|
+
yield pair
|
|
456
|
+
pair = find_predecessor(current_key)
|
|
457
|
+
end
|
|
458
|
+
else
|
|
459
|
+
pair = find_successor(key)
|
|
460
|
+
while pair
|
|
461
|
+
current_key = pair[0]
|
|
462
|
+
yield pair
|
|
463
|
+
pair = find_successor(current_key)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
elsif reverse
|
|
383
467
|
traverse_gt_desc(@root, key, &block)
|
|
384
468
|
else
|
|
385
469
|
traverse_gt(@root, key, &block)
|
|
@@ -391,15 +475,32 @@ class RBTree
|
|
|
391
475
|
#
|
|
392
476
|
# @param key [Object] the lower bound (inclusive)
|
|
393
477
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
478
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
394
479
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
395
480
|
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
396
481
|
# @example
|
|
397
482
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
|
|
398
483
|
# tree.gte(2).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
399
484
|
# tree.gte(2, reverse: true).first # => [4, "four"]
|
|
400
|
-
def gte(key, reverse: false, &block)
|
|
401
|
-
return enum_for(:gte, key, reverse: reverse) unless block_given?
|
|
402
|
-
if
|
|
485
|
+
def gte(key, reverse: false, safe: false, &block)
|
|
486
|
+
return enum_for(:gte, key, reverse: reverse, safe: safe) unless block_given?
|
|
487
|
+
if safe
|
|
488
|
+
if reverse
|
|
489
|
+
pair = find_max
|
|
490
|
+
while pair && pair[0] >= key
|
|
491
|
+
current_key = pair[0]
|
|
492
|
+
yield pair
|
|
493
|
+
pair = find_predecessor(current_key)
|
|
494
|
+
end
|
|
495
|
+
else
|
|
496
|
+
pair = @hash_index[key]&.pair || find_successor(key)
|
|
497
|
+
while pair
|
|
498
|
+
current_key = pair[0]
|
|
499
|
+
yield pair
|
|
500
|
+
pair = find_successor(current_key)
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
elsif reverse
|
|
403
504
|
traverse_gte_desc(@root, key, &block)
|
|
404
505
|
else
|
|
405
506
|
traverse_gte(@root, key, &block)
|
|
@@ -414,15 +515,34 @@ class RBTree
|
|
|
414
515
|
# @param include_min [Boolean] whether to include the lower bound (default: true)
|
|
415
516
|
# @param include_max [Boolean] whether to include the upper bound (default: true)
|
|
416
517
|
# @param reverse [Boolean] if true, iterate in descending order (default: false)
|
|
518
|
+
# @param safe [Boolean] if true, safe for modifications during iteration (default: false)
|
|
417
519
|
# @yield [key, value] each matching key-value pair (if block given)
|
|
418
520
|
# @return [Enumerator, RBTree] Enumerator if no block given, self otherwise
|
|
419
521
|
# @example
|
|
420
522
|
# tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'})
|
|
421
523
|
# tree.between(2, 4).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
422
524
|
# tree.between(2, 4, reverse: true).first # => [4, "four"]
|
|
423
|
-
def between(min, max, include_min: true, include_max: true, reverse: false, &block)
|
|
424
|
-
return enum_for(:between, min, max, include_min: include_min, include_max: include_max, reverse: reverse) unless block_given?
|
|
425
|
-
if
|
|
525
|
+
def between(min, max, include_min: true, include_max: true, reverse: false, safe: false, &block)
|
|
526
|
+
return enum_for(:between, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
|
|
527
|
+
if safe
|
|
528
|
+
if reverse
|
|
529
|
+
pair = include_max && @hash_index[max]&.pair || find_predecessor(max)
|
|
530
|
+
while pair && pair[0] > min
|
|
531
|
+
current_key = pair[0]
|
|
532
|
+
yield pair
|
|
533
|
+
pair = find_predecessor(current_key)
|
|
534
|
+
end
|
|
535
|
+
yield pair if pair && include_min && pair[0] == min
|
|
536
|
+
else
|
|
537
|
+
pair = include_min && @hash_index[min]&.pair || find_successor(min)
|
|
538
|
+
while pair && pair[0] < max
|
|
539
|
+
current_key = pair[0]
|
|
540
|
+
yield pair
|
|
541
|
+
pair = find_successor(current_key)
|
|
542
|
+
end
|
|
543
|
+
yield pair if pair && include_max && pair[0] == max
|
|
544
|
+
end
|
|
545
|
+
elsif reverse
|
|
426
546
|
traverse_between_desc(@root, min, max, include_min, include_max, &block)
|
|
427
547
|
else
|
|
428
548
|
traverse_between(@root, min, max, include_min, include_max, &block)
|
|
@@ -445,7 +565,7 @@ class RBTree
|
|
|
445
565
|
def nearest(key)
|
|
446
566
|
return nil unless key.respond_to?(:-)
|
|
447
567
|
n = find_nearest_node(key)
|
|
448
|
-
n == @nil_node ? nil :
|
|
568
|
+
n == @nil_node ? nil : n.pair
|
|
449
569
|
end
|
|
450
570
|
|
|
451
571
|
# Returns the key-value pair with the largest key that is smaller than the given key.
|
|
@@ -462,7 +582,7 @@ class RBTree
|
|
|
462
582
|
# tree.prev(1) # => nil (no predecessor)
|
|
463
583
|
def prev(key)
|
|
464
584
|
n = find_predecessor_node(key)
|
|
465
|
-
n == @nil_node ? nil :
|
|
585
|
+
n == @nil_node ? nil : n.pair
|
|
466
586
|
end
|
|
467
587
|
|
|
468
588
|
# Returns the key-value pair with the smallest key that is larger than the given key.
|
|
@@ -479,7 +599,7 @@ class RBTree
|
|
|
479
599
|
# tree.succ(7) # => nil (no successor)
|
|
480
600
|
def succ(key)
|
|
481
601
|
n = find_successor_node(key)
|
|
482
|
-
n == @nil_node ? nil :
|
|
602
|
+
n == @nil_node ? nil : n.pair
|
|
483
603
|
end
|
|
484
604
|
|
|
485
605
|
# Validates the red-black tree properties.
|
|
@@ -515,7 +635,7 @@ class RBTree
|
|
|
515
635
|
current = current.left
|
|
516
636
|
end
|
|
517
637
|
current = stack.pop
|
|
518
|
-
yield current.
|
|
638
|
+
yield current.pair
|
|
519
639
|
current = current.right
|
|
520
640
|
end
|
|
521
641
|
end
|
|
@@ -534,7 +654,7 @@ class RBTree
|
|
|
534
654
|
current = current.right
|
|
535
655
|
end
|
|
536
656
|
current = stack.pop
|
|
537
|
-
yield current.
|
|
657
|
+
yield current.pair
|
|
538
658
|
current = current.left
|
|
539
659
|
end
|
|
540
660
|
end
|
|
@@ -550,7 +670,7 @@ class RBTree
|
|
|
550
670
|
|
|
551
671
|
traverse_lt(node.left, key, &block)
|
|
552
672
|
if (node.key <=> key) < 0
|
|
553
|
-
yield node.
|
|
673
|
+
yield node.pair
|
|
554
674
|
traverse_lt(node.right, key, &block)
|
|
555
675
|
end
|
|
556
676
|
end
|
|
@@ -566,7 +686,7 @@ class RBTree
|
|
|
566
686
|
|
|
567
687
|
traverse_lte(node.left, key, &block)
|
|
568
688
|
if (node.key <=> key) <= 0
|
|
569
|
-
yield node.
|
|
689
|
+
yield node.pair
|
|
570
690
|
traverse_lte(node.right, key, &block)
|
|
571
691
|
end
|
|
572
692
|
end
|
|
@@ -582,7 +702,7 @@ class RBTree
|
|
|
582
702
|
|
|
583
703
|
if (node.key <=> key) > 0
|
|
584
704
|
traverse_gt(node.left, key, &block)
|
|
585
|
-
yield node.
|
|
705
|
+
yield node.pair
|
|
586
706
|
end
|
|
587
707
|
traverse_gt(node.right, key, &block)
|
|
588
708
|
end
|
|
@@ -598,7 +718,7 @@ class RBTree
|
|
|
598
718
|
|
|
599
719
|
if (node.key <=> key) >= 0
|
|
600
720
|
traverse_gte(node.left, key, &block)
|
|
601
|
-
yield node.
|
|
721
|
+
yield node.pair
|
|
602
722
|
end
|
|
603
723
|
traverse_gte(node.right, key, &block)
|
|
604
724
|
end
|
|
@@ -622,7 +742,7 @@ class RBTree
|
|
|
622
742
|
less = include_max ? (node.key <=> max) <= 0 : (node.key <=> max) < 0
|
|
623
743
|
|
|
624
744
|
if greater && less
|
|
625
|
-
yield node.
|
|
745
|
+
yield node.pair
|
|
626
746
|
end
|
|
627
747
|
|
|
628
748
|
if (node.key <=> max) < 0
|
|
@@ -641,7 +761,7 @@ class RBTree
|
|
|
641
761
|
|
|
642
762
|
if (node.key <=> key) < 0
|
|
643
763
|
traverse_lt_desc(node.right, key, &block)
|
|
644
|
-
yield node.
|
|
764
|
+
yield node.pair
|
|
645
765
|
end
|
|
646
766
|
traverse_lt_desc(node.left, key, &block)
|
|
647
767
|
end
|
|
@@ -657,7 +777,7 @@ class RBTree
|
|
|
657
777
|
|
|
658
778
|
if (node.key <=> key) <= 0
|
|
659
779
|
traverse_lte_desc(node.right, key, &block)
|
|
660
|
-
yield node.
|
|
780
|
+
yield node.pair
|
|
661
781
|
end
|
|
662
782
|
traverse_lte_desc(node.left, key, &block)
|
|
663
783
|
end
|
|
@@ -673,7 +793,7 @@ class RBTree
|
|
|
673
793
|
|
|
674
794
|
traverse_gt_desc(node.right, key, &block)
|
|
675
795
|
if (node.key <=> key) > 0
|
|
676
|
-
yield node.
|
|
796
|
+
yield node.pair
|
|
677
797
|
traverse_gt_desc(node.left, key, &block)
|
|
678
798
|
end
|
|
679
799
|
end
|
|
@@ -689,7 +809,7 @@ class RBTree
|
|
|
689
809
|
|
|
690
810
|
traverse_gte_desc(node.right, key, &block)
|
|
691
811
|
if (node.key <=> key) >= 0
|
|
692
|
-
yield node.
|
|
812
|
+
yield node.pair
|
|
693
813
|
traverse_gte_desc(node.left, key, &block)
|
|
694
814
|
end
|
|
695
815
|
end
|
|
@@ -714,7 +834,7 @@ class RBTree
|
|
|
714
834
|
less = include_max ? (node.key <=> max) <= 0 : (node.key <=> max) < 0
|
|
715
835
|
|
|
716
836
|
if greater && less
|
|
717
|
-
yield node.
|
|
837
|
+
yield node.pair
|
|
718
838
|
end
|
|
719
839
|
|
|
720
840
|
if (node.key <=> min) > 0
|
|
@@ -1011,6 +1131,16 @@ class RBTree
|
|
|
1011
1131
|
predecessor
|
|
1012
1132
|
end
|
|
1013
1133
|
|
|
1134
|
+
# Finds the node with the largest key that is smaller than the given key.
|
|
1135
|
+
# Returns the key-value pair if found, or nil if the tree is empty.
|
|
1136
|
+
#
|
|
1137
|
+
# @param key [Object] the reference key
|
|
1138
|
+
# @return [Array(Object, Object), nil] the key-value pair, or nil if tree is empty
|
|
1139
|
+
def find_predecessor(key)
|
|
1140
|
+
n = find_predecessor_node(key)
|
|
1141
|
+
n == @nil_node ? nil : n.pair
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1014
1144
|
# Finds the node with the smallest key that is larger than the given key.
|
|
1015
1145
|
#
|
|
1016
1146
|
# If the key exists in the tree, returns its successor node.
|
|
@@ -1051,6 +1181,16 @@ class RBTree
|
|
|
1051
1181
|
successor
|
|
1052
1182
|
end
|
|
1053
1183
|
|
|
1184
|
+
# Finds the node with the smallest key that is larger than the given key.
|
|
1185
|
+
# Returns the key-value pair if found, or nil if the tree is empty.
|
|
1186
|
+
#
|
|
1187
|
+
# @param key [Object] the reference key
|
|
1188
|
+
# @return [Array(Object, Object), nil] the key-value pair, or nil if tree is empty
|
|
1189
|
+
def find_successor(key)
|
|
1190
|
+
n = find_successor_node(key)
|
|
1191
|
+
n == @nil_node ? nil : n.pair
|
|
1192
|
+
end
|
|
1193
|
+
|
|
1054
1194
|
# Finds the leftmost (minimum) node in a subtree.
|
|
1055
1195
|
#
|
|
1056
1196
|
# @param node [Node] the root of the subtree
|
|
@@ -1062,6 +1202,11 @@ class RBTree
|
|
|
1062
1202
|
node
|
|
1063
1203
|
end
|
|
1064
1204
|
|
|
1205
|
+
# Returns the minimum key-value pair.
|
|
1206
|
+
#
|
|
1207
|
+
# @return [Array(Object, Object), nil] a two-element array [key, value], or nil if tree is empty
|
|
1208
|
+
def find_min = @min_node == @nil_node ? nil : @min_node.pair
|
|
1209
|
+
|
|
1065
1210
|
# Finds the rightmost (maximum) node in a subtree.
|
|
1066
1211
|
#
|
|
1067
1212
|
# @param node [Node] the root of the subtree
|
|
@@ -1073,6 +1218,11 @@ class RBTree
|
|
|
1073
1218
|
node
|
|
1074
1219
|
end
|
|
1075
1220
|
|
|
1221
|
+
# Returns the maximum key-value pair.
|
|
1222
|
+
#
|
|
1223
|
+
# @return [Array(Object, Object), nil] a two-element array [key, value], or nil if tree is empty
|
|
1224
|
+
def find_max = (n = rightmost(@root)) == @nil_node ? nil : n.pair
|
|
1225
|
+
|
|
1076
1226
|
# Performs a left rotation on the given node.
|
|
1077
1227
|
#
|
|
1078
1228
|
# Transforms the tree structure:
|
|
@@ -1304,8 +1454,8 @@ class MultiRBTree < RBTree
|
|
|
1304
1454
|
# tree.insert(1, 'second')
|
|
1305
1455
|
# tree.get_all(1).to_a # => ["first", "second"]
|
|
1306
1456
|
def get_all(key)
|
|
1307
|
-
|
|
1308
|
-
|
|
1457
|
+
return enum_for(:get_all, key) unless block_given?
|
|
1458
|
+
@hash_index[key]&.value&.each { |v| yield v }
|
|
1309
1459
|
end
|
|
1310
1460
|
|
|
1311
1461
|
# Inserts a value for the given key.
|
|
@@ -1461,23 +1611,81 @@ class MultiRBTree < RBTree
|
|
|
1461
1611
|
[n.key, last ? n.value.last : n.value.first]
|
|
1462
1612
|
end
|
|
1463
1613
|
|
|
1464
|
-
def nearest(key)
|
|
1614
|
+
def nearest(key, last: false)
|
|
1465
1615
|
n = find_nearest_node(key)
|
|
1466
|
-
n == @nil_node || n.value.empty? ? nil : [n.key, n.value.first]
|
|
1467
|
-
end
|
|
1468
|
-
|
|
1469
|
-
def prev(key, last: false)
|
|
1470
|
-
n = find_predecessor_node(key)
|
|
1471
1616
|
return nil if n == @nil_node || n.value.empty?
|
|
1472
1617
|
[n.key, last ? n.value.last : n.value.first]
|
|
1473
1618
|
end
|
|
1474
1619
|
|
|
1475
|
-
def
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1620
|
+
def each(reverse: false, safe: false, &block)
|
|
1621
|
+
return enum_for(:each, reverse: reverse, safe: safe) unless block_given?
|
|
1622
|
+
if safe
|
|
1623
|
+
super do |k, vals|
|
|
1624
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1625
|
+
end
|
|
1626
|
+
else
|
|
1627
|
+
super
|
|
1628
|
+
end
|
|
1479
1629
|
end
|
|
1480
1630
|
|
|
1631
|
+
def lt(key, reverse: false, safe: false, &block)
|
|
1632
|
+
return enum_for(:lt, key, reverse: reverse, safe: safe) unless block_given?
|
|
1633
|
+
if safe
|
|
1634
|
+
super do |k, vals|
|
|
1635
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1636
|
+
end
|
|
1637
|
+
else
|
|
1638
|
+
super
|
|
1639
|
+
end
|
|
1640
|
+
end
|
|
1641
|
+
|
|
1642
|
+
def lte(key, reverse: false, safe: false, &block)
|
|
1643
|
+
return enum_for(:lte, key, reverse: reverse, safe: safe) unless block_given?
|
|
1644
|
+
if safe
|
|
1645
|
+
super do |k, vals|
|
|
1646
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1647
|
+
end
|
|
1648
|
+
else
|
|
1649
|
+
super
|
|
1650
|
+
end
|
|
1651
|
+
end
|
|
1652
|
+
|
|
1653
|
+
def gt(key, reverse: false, safe: false, &block)
|
|
1654
|
+
return enum_for(:gt, key, reverse: reverse, safe: safe) unless block_given?
|
|
1655
|
+
if safe
|
|
1656
|
+
super do |k, vals|
|
|
1657
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1658
|
+
end
|
|
1659
|
+
else
|
|
1660
|
+
super
|
|
1661
|
+
end
|
|
1662
|
+
end
|
|
1663
|
+
|
|
1664
|
+
def gte(key, reverse: false, safe: false, &block)
|
|
1665
|
+
return enum_for(:gte, key, reverse: reverse, safe: safe) unless block_given?
|
|
1666
|
+
if safe
|
|
1667
|
+
super do |k, vals|
|
|
1668
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1669
|
+
end
|
|
1670
|
+
else
|
|
1671
|
+
super
|
|
1672
|
+
end
|
|
1673
|
+
end
|
|
1674
|
+
|
|
1675
|
+
def between(min, max, include_min: true, include_max: true, reverse: false, safe: false, &block)
|
|
1676
|
+
return enum_for(:between, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
|
|
1677
|
+
if safe
|
|
1678
|
+
super do |k, vals|
|
|
1679
|
+
vals.send(reverse ? :reverse_each : :each) { |v| yield k, v }
|
|
1680
|
+
end
|
|
1681
|
+
else
|
|
1682
|
+
super
|
|
1683
|
+
end
|
|
1684
|
+
end
|
|
1685
|
+
|
|
1686
|
+
def prev(key, last: false) = (n = super) && [n.key, last ? n.value.last : n.value.first]
|
|
1687
|
+
def succ(key, last: false) = (n = super) && [n.key, last ? n.value.last : n.value.first]
|
|
1688
|
+
|
|
1481
1689
|
private
|
|
1482
1690
|
|
|
1483
1691
|
def traverse_asc(node, &block)
|
|
@@ -1677,4 +1885,8 @@ class RBTree::Node
|
|
|
1677
1885
|
# Checks if the node is black.
|
|
1678
1886
|
# @return [Boolean] true if black, false otherwise
|
|
1679
1887
|
def black? = @color == BLACK
|
|
1888
|
+
|
|
1889
|
+
# Returns the key-value pair.
|
|
1890
|
+
# @return [Array(Object, Object)] the key-value pair
|
|
1891
|
+
def pair = [key, value]
|
|
1680
1892
|
end
|
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.1
|
|
4
|
+
version: 0.2.1
|
|
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: []
|