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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 047f125ed11edef5fa998d90ff50d80f90879a065d6407b02184d21089f0b1a8
4
- data.tar.gz: 1bb83c26b7aca89d6cce328298f51d9ba9c3e1e5b7fc1ea2adf3b59c88bf2c40
3
+ metadata.gz: bc2846050227a83e7ae7bb63d1e6208ed39acc54de759c98e08d994685d1b067
4
+ data.tar.gz: 2a28105ee3667fb794c1f5d32c396b21542f45bceb3c858c160d7389f6d2ae3b
5
5
  SHA512:
6
- metadata.gz: '0847bf075101301c924d093b9561c6fe014efbd9bfbc94020427c0382ab38608c275f5e50e0df7221c21b21afc2282de920273bc22dbbb11db2822a367cabc8f'
7
- data.tar.gz: 5b0bc3e6f05ffa239b0316c8927380eddfb322b5e207001930354f5482748f0b148bfd2a42b8796ae83fd19f0b7dd97d9f5b863fcc1500094406b7731bbf3c78
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.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.1.0
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) # => LinkedList with ["first one", "second one", "third one"]
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}" }
@@ -2,5 +2,5 @@
2
2
 
3
3
  class RBTree
4
4
  # The version of the rbtree-ruby gem
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.3"
6
6
  end
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.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
- find_node(key) != @nil_node
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
- n = find_node(key)
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 = find_node(key)
626
- return nil if z == @nil_node
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
- # a linked list of values rather than a single value. The size reflects the total
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 linked lists
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.0
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 [LinkedList, nil] a LinkedList containing all values, or nil if not found
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, LinkedList[value], Node::RED, @nil_node, @nil_node, @nil_node)
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 = find_node(key)
1062
- return nil if z == @nil_node
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 [LinkedList, nil] the list of all deleted values, or nil if not found
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 = find_node(key)
1086
- return nil if z == @nil_node
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
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.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahito Suzuki