rbtree-ruby 0.1.1 → 0.1.3
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 +26 -1
- data/README.md +1 -1
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +20 -127
- 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: bc2846050227a83e7ae7bb63d1e6208ed39acc54de759c98e08d994685d1b067
|
|
4
|
+
data.tar.gz: 2a28105ee3667fb794c1f5d32c396b21542f45bceb3c858c160d7389f6d2ae3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22239d22c7b771a93bbb1811407d6ea6364ae21b22008d53d44e102188991b7299b6dacdcbc28a206dbfd21658d8081f964bf855b7c69c3d19d3312be49ffe3d
|
|
7
|
+
data.tar.gz: 8f98487332a3cb77f478f745b775b148aff862ab80fc020d064cade6a84692a4e498854cce864b4d49806a760ee7c6d9c38e4e9ac3e5c7598444ead625e91eb7
|
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.1.3] - 2026-01-13
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Hybrid Hash Index**: Added internal `@hash_index` for O(1) key lookup
|
|
12
|
+
- `get(key)` and `has_key?(key)` now use hash lookup instead of tree traversal
|
|
13
|
+
- Search performance now matches Hash (within 10-20% overhead)
|
|
14
|
+
- Benchmark: 10M elements, 1M lookups - RBTree 290ms vs Hash 271ms
|
|
15
|
+
- Both RBTree and MultiRBTree benefit from this optimization
|
|
16
|
+
- Memory trade-off: ~1.5x due to hash index storage
|
|
17
|
+
|
|
18
|
+
## [0.1.2] - 2026-01-13
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Replaced `RBTree::LinkedList` with Ruby Array** in MultiRBTree
|
|
22
|
+
- 6-8x performance improvement for all operations (shift, pop, append)
|
|
23
|
+
- Removed `RBTree::LinkedList` and `RBTree::LinkedList::Node` classes
|
|
24
|
+
- `get_all` now returns Array instead of LinkedList
|
|
25
|
+
- Benchmark results: 100K shift operations 452ms → 69ms
|
|
26
|
+
|
|
27
|
+
## [0.1.1] - 2026-01-13
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Fixed syntax error in `RBTree::LinkedList.[]` method (escaped newline issue)
|
|
31
|
+
|
|
8
32
|
## [0.1.0] - 2026-01-13
|
|
9
33
|
|
|
10
34
|
### Added
|
|
@@ -35,4 +59,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
35
59
|
- ASCII diagrams for tree rotation operations
|
|
36
60
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
37
61
|
|
|
38
|
-
[0.1.
|
|
62
|
+
[0.1.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.3
|
|
63
|
+
[0.1.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.2
|
data/README.md
CHANGED
|
@@ -99,7 +99,7 @@ tree.get(1) # => "first one"
|
|
|
99
99
|
tree[1] # => "first one"
|
|
100
100
|
|
|
101
101
|
# Get all values for a key
|
|
102
|
-
tree.get_all(1) # =>
|
|
102
|
+
tree.get_all(1) # => ["first one", "second one", "third one"]
|
|
103
103
|
|
|
104
104
|
# Iterate over all key-value pairs
|
|
105
105
|
tree.each { |k, v| puts "#{k}: #{v}" }
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -50,7 +50,7 @@ require_relative "rbtree/version"
|
|
|
50
50
|
# Iteration over all elements takes O(n) time.
|
|
51
51
|
#
|
|
52
52
|
# @author Masahito Suzuki
|
|
53
|
-
# @since 0.1.
|
|
53
|
+
# @since 0.1.0
|
|
54
54
|
class RBTree
|
|
55
55
|
include Enumerable
|
|
56
56
|
|
|
@@ -92,6 +92,7 @@ class RBTree
|
|
|
92
92
|
@nil_node.right = @nil_node
|
|
93
93
|
@root = @nil_node
|
|
94
94
|
@min_node = @nil_node
|
|
95
|
+
@hash_index = {} # Hash index for O(1) key lookup
|
|
95
96
|
@size = 0
|
|
96
97
|
|
|
97
98
|
if args.any?
|
|
@@ -139,7 +140,7 @@ class RBTree
|
|
|
139
140
|
# tree.has_key?(1) # => true
|
|
140
141
|
# tree.has_key?(3) # => false
|
|
141
142
|
def has_key?(key)
|
|
142
|
-
|
|
143
|
+
@hash_index.key?(key)
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
# Retrieves the value associated with the given key.
|
|
@@ -152,8 +153,7 @@ class RBTree
|
|
|
152
153
|
# tree[2] # => "two"
|
|
153
154
|
# tree[3] # => nil
|
|
154
155
|
def get(key)
|
|
155
|
-
|
|
156
|
-
n == @nil_node ? nil : n.value
|
|
156
|
+
@hash_index[key]&.value
|
|
157
157
|
end
|
|
158
158
|
alias_method :[], :get
|
|
159
159
|
|
|
@@ -207,6 +207,7 @@ class RBTree
|
|
|
207
207
|
@min_node = z
|
|
208
208
|
end
|
|
209
209
|
|
|
210
|
+
@hash_index[key] = z # Add to hash index
|
|
210
211
|
true
|
|
211
212
|
end
|
|
212
213
|
alias_method :[]=, :insert
|
|
@@ -232,6 +233,7 @@ class RBTree
|
|
|
232
233
|
def clear
|
|
233
234
|
@root = @nil_node
|
|
234
235
|
@min_node = @nil_node
|
|
236
|
+
@hash_index.clear
|
|
235
237
|
@size = 0
|
|
236
238
|
self
|
|
237
239
|
end
|
|
@@ -622,8 +624,8 @@ class RBTree
|
|
|
622
624
|
# @param key [Object] the key to delete
|
|
623
625
|
# @return [Object, nil] the value of the deleted node, or nil if not found
|
|
624
626
|
def delete_node(key)
|
|
625
|
-
z =
|
|
626
|
-
return nil
|
|
627
|
+
z = @hash_index.delete(key) # O(1) lookup and remove from index
|
|
628
|
+
return nil unless z
|
|
627
629
|
remove_node(z)
|
|
628
630
|
end
|
|
629
631
|
|
|
@@ -936,12 +938,12 @@ end
|
|
|
936
938
|
# A Multi Red-Black Tree that allows duplicate keys.
|
|
937
939
|
#
|
|
938
940
|
# MultiRBTree extends RBTree to support multiple values per key. Each key maps to
|
|
939
|
-
#
|
|
941
|
+
# an array of values rather than a single value. The size reflects the total
|
|
940
942
|
# number of key-value pairs (not unique keys).
|
|
941
943
|
#
|
|
942
944
|
# == Features
|
|
943
945
|
#
|
|
944
|
-
# * Multiple values per key using
|
|
946
|
+
# * Multiple values per key using arrays
|
|
945
947
|
# * Separate methods for single deletion (`delete_one`) vs. all deletions (`delete`)
|
|
946
948
|
# * Values for each key maintain insertion order
|
|
947
949
|
# * min/max return first/last values respectively
|
|
@@ -963,7 +965,7 @@ end
|
|
|
963
965
|
# tree.delete(1) # removes all remaining values for key 1
|
|
964
966
|
#
|
|
965
967
|
# @author Masahito Suzuki
|
|
966
|
-
# @since 0.1.
|
|
968
|
+
# @since 0.1.2
|
|
967
969
|
class MultiRBTree < RBTree
|
|
968
970
|
# Retrieves the first value associated with the given key.
|
|
969
971
|
#
|
|
@@ -983,7 +985,7 @@ class MultiRBTree < RBTree
|
|
|
983
985
|
# Retrieves all values associated with the given key.
|
|
984
986
|
#
|
|
985
987
|
# @param key [Object] the key to look up
|
|
986
|
-
# @return [
|
|
988
|
+
# @return [Array, nil] an Array containing all values, or nil if not found
|
|
987
989
|
# @example
|
|
988
990
|
# tree = MultiRBTree.new
|
|
989
991
|
# tree.insert(1, 'first')
|
|
@@ -1022,7 +1024,7 @@ class MultiRBTree < RBTree
|
|
|
1022
1024
|
x = x.right
|
|
1023
1025
|
end
|
|
1024
1026
|
end
|
|
1025
|
-
z = Node.new(key,
|
|
1027
|
+
z = Node.new(key, [value], Node::RED, @nil_node, @nil_node, @nil_node)
|
|
1026
1028
|
z.parent = y
|
|
1027
1029
|
if y == @nil_node
|
|
1028
1030
|
@root = z
|
|
@@ -1041,6 +1043,7 @@ class MultiRBTree < RBTree
|
|
|
1041
1043
|
@min_node = z
|
|
1042
1044
|
end
|
|
1043
1045
|
|
|
1046
|
+
@hash_index[key] = z # Add to hash index
|
|
1044
1047
|
true
|
|
1045
1048
|
end
|
|
1046
1049
|
|
|
@@ -1058,12 +1061,13 @@ class MultiRBTree < RBTree
|
|
|
1058
1061
|
# tree.delete_one(1) # => "first"
|
|
1059
1062
|
# tree.get(1) # => "second"
|
|
1060
1063
|
def delete_one(key)
|
|
1061
|
-
z =
|
|
1062
|
-
return nil
|
|
1064
|
+
z = @hash_index[key] # O(1) lookup
|
|
1065
|
+
return nil unless z
|
|
1063
1066
|
|
|
1064
1067
|
value = z.value.shift
|
|
1065
1068
|
@size -= 1
|
|
1066
1069
|
if z.value.empty?
|
|
1070
|
+
@hash_index.delete(key) # Remove from index when node removed
|
|
1067
1071
|
remove_node(z)
|
|
1068
1072
|
end
|
|
1069
1073
|
value
|
|
@@ -1074,7 +1078,7 @@ class MultiRBTree < RBTree
|
|
|
1074
1078
|
# Removes the node and all associated values.
|
|
1075
1079
|
#
|
|
1076
1080
|
# @param key [Object] the key to delete
|
|
1077
|
-
# @return [
|
|
1081
|
+
# @return [Array, nil] the array of all deleted values, or nil if not found
|
|
1078
1082
|
# @example
|
|
1079
1083
|
# tree = MultiRBTree.new
|
|
1080
1084
|
# tree.insert(1, 'first')
|
|
@@ -1082,8 +1086,8 @@ class MultiRBTree < RBTree
|
|
|
1082
1086
|
# vals = tree.delete(1) # removes both values
|
|
1083
1087
|
# vals.size # => 2
|
|
1084
1088
|
def delete(key)
|
|
1085
|
-
z =
|
|
1086
|
-
return nil
|
|
1089
|
+
z = @hash_index.delete(key) # O(1) lookup and remove from index
|
|
1090
|
+
return nil unless z
|
|
1087
1091
|
|
|
1088
1092
|
count = z.value.size
|
|
1089
1093
|
remove_node(z)
|
|
@@ -1257,114 +1261,3 @@ class RBTree::Node
|
|
|
1257
1261
|
# @return [Boolean] true if black, false otherwise
|
|
1258
1262
|
def black? = @color == BLACK
|
|
1259
1263
|
end
|
|
1260
|
-
|
|
1261
|
-
# A doubly-linked list used by MultiRBTree to store multiple values per key.
|
|
1262
|
-
#
|
|
1263
|
-
# This internal data structure maintains insertion order and supports efficient
|
|
1264
|
-
# operations at both ends of the list.
|
|
1265
|
-
#
|
|
1266
|
-
# @api private
|
|
1267
|
-
class RBTree::LinkedList
|
|
1268
|
-
# Returns the number of values in the list.
|
|
1269
|
-
# @return [Integer] the size
|
|
1270
|
-
attr_reader :size
|
|
1271
|
-
|
|
1272
|
-
# Creates a new LinkedList from the given values.
|
|
1273
|
-
#
|
|
1274
|
-
# @param values [Array<Object>] initial values
|
|
1275
|
-
# @return [LinkedList] a new list
|
|
1276
|
-
def self.[](*values)
|
|
1277
|
-
list = new
|
|
1278
|
-
values.each do |value|
|
|
1279
|
-
list << value
|
|
1280
|
-
end
|
|
1281
|
-
list
|
|
1282
|
-
end
|
|
1283
|
-
|
|
1284
|
-
# Creates a new empty LinkedList.
|
|
1285
|
-
def initialize
|
|
1286
|
-
@stop = Node.new(nil, nil, nil)
|
|
1287
|
-
@stop.prev = @stop.succ = @stop
|
|
1288
|
-
@size = 0
|
|
1289
|
-
end
|
|
1290
|
-
|
|
1291
|
-
# Iterates over values in forward order.
|
|
1292
|
-
# @yield [value] each value in the list
|
|
1293
|
-
def each
|
|
1294
|
-
node = @stop.succ
|
|
1295
|
-
while node != @stop
|
|
1296
|
-
yield node.value
|
|
1297
|
-
node = node.succ
|
|
1298
|
-
end
|
|
1299
|
-
end
|
|
1300
|
-
|
|
1301
|
-
# Iterates over values in reverse order.
|
|
1302
|
-
# @yield [value] each value in reverse
|
|
1303
|
-
def reverse_each
|
|
1304
|
-
node = @stop.prev
|
|
1305
|
-
while node != @stop
|
|
1306
|
-
yield node.value
|
|
1307
|
-
node = node.prev
|
|
1308
|
-
end
|
|
1309
|
-
end
|
|
1310
|
-
|
|
1311
|
-
# Checks if the list is empty.
|
|
1312
|
-
# @return [Boolean] true if empty
|
|
1313
|
-
def empty? = @stop.succ == @stop
|
|
1314
|
-
|
|
1315
|
-
# Returns the first value.
|
|
1316
|
-
# @return [Object, nil] the first value or nil if empty
|
|
1317
|
-
def first = empty? ? nil : @stop.succ.value
|
|
1318
|
-
|
|
1319
|
-
# Returns the last value.
|
|
1320
|
-
# @return [Object, nil] the last value or nil if empty
|
|
1321
|
-
def last = empty? ? nil : @stop.prev.value
|
|
1322
|
-
|
|
1323
|
-
# Appends a value to the end of the list.
|
|
1324
|
-
# @param value [Object] the value to append
|
|
1325
|
-
# @return [LinkedList] self
|
|
1326
|
-
def <<(value)
|
|
1327
|
-
@stop.prev = @stop.prev.succ = Node.new(value, @stop.prev, @stop)
|
|
1328
|
-
@size += 1
|
|
1329
|
-
self
|
|
1330
|
-
end
|
|
1331
|
-
|
|
1332
|
-
# Removes and returns the first value.
|
|
1333
|
-
# @return [Object, nil] the first value or nil if empty
|
|
1334
|
-
def shift
|
|
1335
|
-
return nil if empty?
|
|
1336
|
-
value = @stop.succ.value
|
|
1337
|
-
(@stop.succ = @stop.succ.succ).prev = @stop
|
|
1338
|
-
@size -= 1
|
|
1339
|
-
value
|
|
1340
|
-
end
|
|
1341
|
-
|
|
1342
|
-
# Removes and returns the last value.
|
|
1343
|
-
# @return [Object, nil] the last value or nil if empty
|
|
1344
|
-
def pop
|
|
1345
|
-
return nil if empty?
|
|
1346
|
-
value = @stop.prev.value
|
|
1347
|
-
(@stop.prev = @stop.prev.prev).succ = @stop
|
|
1348
|
-
@size -= 1
|
|
1349
|
-
value
|
|
1350
|
-
end
|
|
1351
|
-
end
|
|
1352
|
-
|
|
1353
|
-
# Internal node structure for LinkedList.
|
|
1354
|
-
# @api private
|
|
1355
|
-
class RBTree::LinkedList::Node
|
|
1356
|
-
# @return [Object] the value stored in this node
|
|
1357
|
-
attr_accessor :value
|
|
1358
|
-
# @return [Node] the previous node
|
|
1359
|
-
attr_accessor :prev
|
|
1360
|
-
# @return [Node] the successor node
|
|
1361
|
-
attr_accessor :succ
|
|
1362
|
-
|
|
1363
|
-
# Creates a new list node.
|
|
1364
|
-
# @param value [Object] the value
|
|
1365
|
-
# @param prev [Node] the previous node
|
|
1366
|
-
# @param succ [Node] the successor node
|
|
1367
|
-
def initialize(value, prev, succ)
|
|
1368
|
-
@value, @prev, @succ = value, prev, succ
|
|
1369
|
-
end
|
|
1370
|
-
end
|