rbtree-ruby 0.1.3 → 0.1.5
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 +20 -0
- data/README.md +37 -2
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +76 -19
- 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: 56ddd66545f9e423b96066b12deea8e22fc86b35b7a97be056ebf91f80543e77
|
|
4
|
+
data.tar.gz: 10a3ec88b0a5f5cb199ac96267d7665bfee99ab4fb9cf89488790fe798120b05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d521a30299f8c3db327d1b671d84113cf3c9e5adc90a78747e76b7480ef02ceb95d813a90eeebadfd32e4b8360b2d43fffd1ed1ac4d9314d8368cbe137ce7aa
|
|
7
|
+
data.tar.gz: 445a6cf0a99efdd0b5b4dc41dd86ec219e784cc3a2ecf150b4467e2619bcb08bd9b47d119d1c5310f43fdecdd7dc19941c3bfa4ae9e7b68d2b359819336f0a97
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ 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.5] - 2026-01-13
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Iterative Traversal**: Replaced recursive tree traversal with iterative approach
|
|
12
|
+
- `each`, `reverse_each`, and other traversal methods now use an explicit stack
|
|
13
|
+
- Prevents `SystemStackError` (stack overflow) on extremely deep trees
|
|
14
|
+
- Slightly improves iteration performance by removing recursion overhead
|
|
15
|
+
- Applies to both `RBTree` and `MultiRBTree`
|
|
16
|
+
|
|
17
|
+
## [0.1.4] - 2026-01-13
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Memory Pool**: Implemented internal node recycling mechanism
|
|
21
|
+
- Reuse `RBTree::Node` objects instead of creating new ones for every insertion
|
|
22
|
+
- Significantly reduces GC pressure during frequent insert/delete operations
|
|
23
|
+
- Automatically manages pool size (grows on delete, shrinks on insert)
|
|
24
|
+
- Fully transparent to the user
|
|
25
|
+
|
|
8
26
|
## [0.1.3] - 2026-01-13
|
|
9
27
|
|
|
10
28
|
### Changed
|
|
@@ -59,5 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
59
77
|
- ASCII diagrams for tree rotation operations
|
|
60
78
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
61
79
|
|
|
80
|
+
[0.1.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.5
|
|
81
|
+
[0.1.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.4
|
|
62
82
|
[0.1.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.3
|
|
63
83
|
[0.1.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.2
|
data/README.md
CHANGED
|
@@ -133,12 +133,47 @@ All major operations run in **O(log n)** time:
|
|
|
133
133
|
|
|
134
134
|
- `insert(key, value)` - O(log n)
|
|
135
135
|
- `delete(key)` - O(log n)
|
|
136
|
-
- `get(key)` / `[]` - O(
|
|
137
|
-
- `
|
|
136
|
+
- `get(key)` / `[]` - **O(1)** (hybrid hash index)
|
|
137
|
+
- `has_key?` - **O(1)** (hybrid hash index)
|
|
138
|
+
- `min` - **O(1)**
|
|
139
|
+
- `max` - O(log n)
|
|
138
140
|
- `shift` / `pop` - O(log n)
|
|
139
141
|
|
|
140
142
|
Iteration over all elements takes O(n) time.
|
|
141
143
|
|
|
144
|
+
### Memory Efficiency
|
|
145
|
+
|
|
146
|
+
RBTree uses an internal **Memory Pool** to recycle node objects.
|
|
147
|
+
- Significantly reduces Garbage Collection (GC) pressure during frequent insertions and deletions (e.g., in high-throughput queues).
|
|
148
|
+
- In benchmarks with 100,000 cyclic operations, **GC time was 0.0s** compared to significant pauses without pooling.
|
|
149
|
+
|
|
150
|
+
### RBTree vs Hash vs Array
|
|
151
|
+
|
|
152
|
+
RBTree provides significant advantages for ordered operations:
|
|
153
|
+
|
|
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 |
|
|
161
|
+
|
|
162
|
+
### When to Use RBTree
|
|
163
|
+
|
|
164
|
+
✅ **Use RBTree when you need:**
|
|
165
|
+
- Ordered iteration by key
|
|
166
|
+
- Fast min/max retrieval
|
|
167
|
+
- Range queries (`between`, `lt`, `gt`, `lte`, `gte`)
|
|
168
|
+
- Nearest key search
|
|
169
|
+
- Priority queue behavior (shift/pop by key order)
|
|
170
|
+
|
|
171
|
+
✅ **Use Hash when you only need:**
|
|
172
|
+
- Fast key-value lookup (RBTree is now equally fast!)
|
|
173
|
+
- No ordering requirements
|
|
174
|
+
|
|
175
|
+
Run `ruby demo.rb` for a full benchmark demonstration.
|
|
176
|
+
|
|
142
177
|
## API Documentation
|
|
143
178
|
|
|
144
179
|
Full RDoc documentation is available. Generate it locally with:
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -93,6 +93,7 @@ class RBTree
|
|
|
93
93
|
@root = @nil_node
|
|
94
94
|
@min_node = @nil_node
|
|
95
95
|
@hash_index = {} # Hash index for O(1) key lookup
|
|
96
|
+
@node_pool = [] # Memory pool for recycling nodes
|
|
96
97
|
@size = 0
|
|
97
98
|
|
|
98
99
|
if args.any?
|
|
@@ -188,7 +189,7 @@ class RBTree
|
|
|
188
189
|
x = x.right
|
|
189
190
|
end
|
|
190
191
|
end
|
|
191
|
-
z =
|
|
192
|
+
z = allocate_node(key, value, Node::RED, @nil_node, @nil_node, @nil_node)
|
|
192
193
|
z.parent = y
|
|
193
194
|
if y == @nil_node
|
|
194
195
|
@root = z
|
|
@@ -464,10 +465,17 @@ class RBTree
|
|
|
464
465
|
# @yield [key, value] each key-value pair in ascending order
|
|
465
466
|
# @return [void]
|
|
466
467
|
def traverse_asc(node, &block)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
468
|
+
stack = []
|
|
469
|
+
current = node
|
|
470
|
+
while current != @nil_node || !stack.empty?
|
|
471
|
+
while current != @nil_node
|
|
472
|
+
stack << current
|
|
473
|
+
current = current.left
|
|
474
|
+
end
|
|
475
|
+
current = stack.pop
|
|
476
|
+
yield current.key, current.value
|
|
477
|
+
current = current.right
|
|
478
|
+
end
|
|
471
479
|
end
|
|
472
480
|
|
|
473
481
|
# Traverses the tree in descending order (reverse in-order traversal).
|
|
@@ -476,10 +484,17 @@ class RBTree
|
|
|
476
484
|
# @yield [key, value] each key-value pair in descending order
|
|
477
485
|
# @return [void]
|
|
478
486
|
def traverse_desc(node, &block)
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
487
|
+
stack = []
|
|
488
|
+
current = node
|
|
489
|
+
while current != @nil_node || !stack.empty?
|
|
490
|
+
while current != @nil_node
|
|
491
|
+
stack << current
|
|
492
|
+
current = current.right
|
|
493
|
+
end
|
|
494
|
+
current = stack.pop
|
|
495
|
+
yield current.key, current.value
|
|
496
|
+
current = current.left
|
|
497
|
+
end
|
|
483
498
|
end
|
|
484
499
|
|
|
485
500
|
# Traverses nodes with keys less than the specified key.
|
|
@@ -680,7 +695,9 @@ class RBTree
|
|
|
680
695
|
@min_node = next_min_node
|
|
681
696
|
end
|
|
682
697
|
|
|
683
|
-
z.value
|
|
698
|
+
value = z.value
|
|
699
|
+
release_node(z)
|
|
700
|
+
value
|
|
684
701
|
end
|
|
685
702
|
|
|
686
703
|
# Restores red-black tree properties after deletion.
|
|
@@ -900,6 +917,32 @@ class RBTree
|
|
|
900
917
|
y.parent = x
|
|
901
918
|
end
|
|
902
919
|
|
|
920
|
+
# Allocates a new node or recycles one from the pool.
|
|
921
|
+
# @return [Node]
|
|
922
|
+
def allocate_node(key, value, color, left, right, parent)
|
|
923
|
+
if (node = @node_pool.pop)
|
|
924
|
+
node.key = key
|
|
925
|
+
node.value = value
|
|
926
|
+
node.color = color
|
|
927
|
+
node.left = left
|
|
928
|
+
node.right = right
|
|
929
|
+
node.parent = parent
|
|
930
|
+
node
|
|
931
|
+
else
|
|
932
|
+
Node.new(key, value, color, left, right, parent)
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
# Releases a node back to the pool.
|
|
937
|
+
# @param node [Node] the node to release
|
|
938
|
+
def release_node(node)
|
|
939
|
+
node.left = nil
|
|
940
|
+
node.right = nil
|
|
941
|
+
node.parent = nil
|
|
942
|
+
node.value = nil # Help GC
|
|
943
|
+
@node_pool << node
|
|
944
|
+
end
|
|
945
|
+
|
|
903
946
|
# Recursively checks black height consistency.
|
|
904
947
|
#
|
|
905
948
|
# Verifies that:
|
|
@@ -1024,7 +1067,7 @@ class MultiRBTree < RBTree
|
|
|
1024
1067
|
x = x.right
|
|
1025
1068
|
end
|
|
1026
1069
|
end
|
|
1027
|
-
z =
|
|
1070
|
+
z = allocate_node(key, [value], Node::RED, @nil_node, @nil_node, @nil_node)
|
|
1028
1071
|
z.parent = y
|
|
1029
1072
|
if y == @nil_node
|
|
1030
1073
|
@root = z
|
|
@@ -1137,17 +1180,31 @@ class MultiRBTree < RBTree
|
|
|
1137
1180
|
private
|
|
1138
1181
|
|
|
1139
1182
|
def traverse_asc(node, &block)
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1183
|
+
stack = []
|
|
1184
|
+
current = node
|
|
1185
|
+
while current != @nil_node || !stack.empty?
|
|
1186
|
+
while current != @nil_node
|
|
1187
|
+
stack << current
|
|
1188
|
+
current = current.left
|
|
1189
|
+
end
|
|
1190
|
+
current = stack.pop
|
|
1191
|
+
current.value.each { |v| yield current.key, v }
|
|
1192
|
+
current = current.right
|
|
1193
|
+
end
|
|
1144
1194
|
end
|
|
1145
1195
|
|
|
1146
1196
|
def traverse_desc(node, &block)
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1197
|
+
stack = []
|
|
1198
|
+
current = node
|
|
1199
|
+
while current != @nil_node || !stack.empty?
|
|
1200
|
+
while current != @nil_node
|
|
1201
|
+
stack << current
|
|
1202
|
+
current = current.right
|
|
1203
|
+
end
|
|
1204
|
+
current = stack.pop
|
|
1205
|
+
current.value.reverse_each { |v| yield current.key, v }
|
|
1206
|
+
current = current.left
|
|
1207
|
+
end
|
|
1151
1208
|
end
|
|
1152
1209
|
|
|
1153
1210
|
def traverse_lt(node, key, &block)
|