red-black-tree 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: 61a6b03da9c3a60295a236ee3a62996b780cb19dfc18f85fd050e5eb788cf2a0
4
- data.tar.gz: 05a1325effc0127636d0b9ad4e837e1b319c248488b20b4023541bbdea940f29
3
+ metadata.gz: 2b536af387a65d00a6d05596a8b209b32b500abf5cf156d5ab7169ab0149f6b2
4
+ data.tar.gz: 0bc6b9317ee4659228b201d5feb0c3146b356c5831ebe00a003d5d4f491020dc
5
5
  SHA512:
6
- metadata.gz: 19aaf38a058f020b0638ddb99cfa3ecaa51d9bc6a0a0f82046f577898e9de6cf203a3d8e48c2d3dbba60bbf3b555ecd8c47e5cffbe9f8cf0840740af34698924
7
- data.tar.gz: 759e5478416fc7c5a80e4c0775d4df53d9640ddc356b3eea65595ae3657d0a8314c0ff7273799c8f6f0ed31ca6b5bf3e2431b31008f17537445aca2b6b694807
6
+ metadata.gz: ea60c092e82dcecf019933df7348db451e10922437d4ac90c325fb1654ad5226738513b4a4d2e2ee45f19d2af86753c7f5932584a66ec0e90bf8c3ea0ce4836a
7
+ data.tar.gz: c022d30679141753341dee36142b6690756d1797fe11e2dab34b4ddeeaafcc163693a3686bf4e2a324bd60951b869d9cd6d9496d49fed0c2a9fa4c7c3335751a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.3] - 2024-10-21
4
+
5
+ - Make `RedBlackTree#left_most_node` public
6
+ - Add `RedBlackTree#traverse_pre_order`
7
+ - Add `RedBlackTree#traverse_in_order`
8
+ - Add `RedBlackTree#traverse_post_order`
9
+ - Add `RedBlackTree#traverse_level_order`
10
+ - Add `RedBlackTree#traverse`, alias of `RedBlackTree#traverse_in_order`
11
+ - Extend `RedBlackTree#search` to accept a block
12
+ - Delegate missing `RedBlackTree::Node` methods to its `#data`
13
+
14
+ ## [0.1.2] - 2024-09-08
15
+
16
+ - Fix a bunch of issues in `RedBlackTree#insert!` and `RedBlackTree#delete!` algorithms
17
+ - Fix `RedBlackTree::LeafNode`s being marked red
18
+ - Handle comparison with `RedBlackTree::LeafNode` in subclasses of `RedBlackTree::Node`
19
+ - Add `RedBlackTree#include?`
20
+ - Add `RedBlackTree#search`
21
+ - Alias `RedBlackTree#left_most_node` as `RedBlackTree#min`
22
+
3
23
  ## [0.1.1] - 2024-08-04
4
24
 
5
25
  - Update `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` in README
data/README.md CHANGED
@@ -17,20 +17,17 @@ If bundler is not being used to manage dependencies, install the gem by executin
17
17
  ```ruby
18
18
  Work = Struct.new :min_latency, keyword_init: true
19
19
 
20
+ # Needs to be implemented by you
20
21
  class WorkNode < RedBlackTree::Node
21
22
  def <=> other
22
- if other.instance_of? RedBlackTree::LeafNode
23
- 1
24
- else
25
- self.data.min_latency <=> other.data.min_latency
26
- end
23
+ self.data.min_latency <=> other.data.min_latency
27
24
  end
28
25
  end
29
26
 
30
27
  tree = RedBlackTree.new
31
28
 
32
- tree << WorkNode.new(Work.new min_latency: 1.2)
33
29
  tree << WorkNode.new(Work.new min_latency: 0.8)
30
+ tree << WorkNode.new(Work.new min_latency: 1.2)
34
31
  tree << WorkNode.new(Work.new min_latency: 0.8)
35
32
  tree << WorkNode.new(Work.new min_latency: 0.4)
36
33
 
@@ -40,6 +37,158 @@ until tree.empty?
40
37
  end
41
38
  ```
42
39
 
40
+ ## Performance
41
+
42
+ > [!NOTE]
43
+ > Red-black trees are designed for specific use cases and are not intended as a general-purpose data structure. The
44
+ comparisons below are provided merely to illustrate the performance characteristics of the gem. However, it is important
45
+ to note that the benchmarks do not take into account the self-balancing nature of red-black trees.
46
+
47
+ ### Sort and iterate 10,000 elements
48
+
49
+ ```ruby
50
+ require 'benchmark/ips'
51
+
52
+ Work = Struct.new :min_latency, keyword_init: true
53
+
54
+ class WorkNode < RedBlackTree::Node
55
+ def <=> other
56
+ self.data.min_latency <=> other.data.min_latency
57
+ end
58
+ end
59
+
60
+ sample_data = 10_000.times.map { Work.new(min_latency: rand(1_000)) }
61
+
62
+ Benchmark.ips do |x|
63
+ x.report("RedBlackTree") do
64
+ tree = RedBlackTree.new
65
+ sample_data.each { |work| tree << WorkNode.new(work); }
66
+ tree.shift until tree.empty?
67
+ end
68
+
69
+ # 1:1 comparison
70
+ x.report("Array (gradual sort)") do
71
+ array = []
72
+ sample_data.each { |work| array << work; array.sort_by!(&:min_latency); }
73
+ array.shift until array.empty?
74
+ end
75
+
76
+ x.report("Array (single sort)") do
77
+ array = []
78
+ sample_data.each { |work| array << work; }
79
+ array.sort_by!(&:min_latency)
80
+ array.shift until array.empty?
81
+ end
82
+
83
+ x.compare!
84
+ end
85
+
86
+ #=> ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin24]
87
+ #=> Warming up --------------------------------------
88
+ #=> RedBlackTree 1.000 i/100ms
89
+ #=> Array (gradual sort) 1.000 i/100ms
90
+ #=> Array (single sort) 78.000 i/100ms
91
+ #=> Calculating -------------------------------------
92
+ #=> RedBlackTree 5.417 (± 0.0%) i/s (184.61 ms/i) - 28.000 in 5.172532s
93
+ #=> Array (gradual sort) 0.268 (± 0.0%) i/s (3.74 s/i) - 2.000 in 7.473005s
94
+ #=> Array (single sort) 768.691 (± 2.2%) i/s (1.30 ms/i) - 3.900k in 5.076337s
95
+ #=>
96
+ #=> Comparison:
97
+ #=> Array (single sort): 768.7 i/s
98
+ #=> RedBlackTree: 5.4 i/s - 141.91x slower
99
+ #=> Array (gradual sort): 0.3 i/s - 2872.03x slower
100
+ ```
101
+
102
+ ### Sort and search 10,000 elements
103
+
104
+ ```ruby
105
+ require 'benchmark/ips'
106
+
107
+ Work = Struct.new :min_latency, keyword_init: true
108
+
109
+ class WorkNode < RedBlackTree::Node
110
+ def <=> other
111
+ self.data.min_latency <=> other.data.min_latency
112
+ end
113
+ end
114
+
115
+ sample_data = 10_000.times.map { Work.new(min_latency: rand(1_000)) }
116
+ search_sample = sample_data.sample
117
+
118
+ Benchmark.ips do |x|
119
+ x.report("RedBlackTree#search") do
120
+ tree = RedBlackTree.new
121
+ sample_data.each { |work| tree << WorkNode.new(work); }
122
+ raise unless tree.search search_sample
123
+ end
124
+
125
+ # 1:1 comparison
126
+ x.report("Array#find (gradual sort)") do
127
+ array = []
128
+ sample_data.each { |work| array << work; array.sort_by!(&:min_latency); }
129
+ raise unless array.find { |work| work.min_latency == search_sample.min_latency }
130
+ end
131
+
132
+ x.report("Array#find (single sort)") do
133
+ array = []
134
+ sample_data.each { |work| array << work; }
135
+ array.sort_by!(&:min_latency)
136
+ raise unless array.find { |work| work.min_latency == search_sample.min_latency }
137
+ end
138
+
139
+ # 1:1 comparison
140
+ x.report("Array#bsearch (gradual sort)") do
141
+ array = []
142
+ sample_data.each { |work| array << work; array.sort_by!(&:min_latency); }
143
+ raise unless array.bsearch { |work| search_sample.min_latency <= work.min_latency }
144
+ end
145
+
146
+ x.report("Array#bsearch (single sort)") do
147
+ array = []
148
+ sample_data.each { |work| array << work; }
149
+ array.sort_by!(&:min_latency)
150
+ raise unless array.bsearch { |work| search_sample.min_latency <= work.min_latency }
151
+ end
152
+
153
+ x.compare!
154
+ end
155
+
156
+ #=> ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin24]
157
+ #=> Warming up --------------------------------------
158
+ #=> RedBlackTree#search 1.000 i/100ms
159
+ #=> Array#find (gradual sort)
160
+ #=> 1.000 i/100ms
161
+ #=> Array#find (single sort)
162
+ #=> 69.000 i/100ms
163
+ #=> Array#bsearch (gradual sort)
164
+ #=> 1.000 i/100ms
165
+ #=> Array#bsearch (single sort)
166
+ #=> 89.000 i/100ms
167
+ #=> Calculating -------------------------------------
168
+ #=> RedBlackTree#search 12.926 (± 0.0%) i/s (77.36 ms/i) - 65.000 in 5.030736s
169
+ #=> Array#find (gradual sort)
170
+ #=> 0.262 (± 0.0%) i/s (3.81 s/i) - 2.000 in 7.623953s
171
+ #=> Array#find (single sort)
172
+ #=> 690.631 (± 1.0%) i/s (1.45 ms/i) - 3.519k in 5.095823s
173
+ #=> Array#bsearch (gradual sort)
174
+ #=> 0.267 (± 0.0%) i/s (3.75 s/i) - 2.000 in 7.492482s
175
+ #=> Array#bsearch (single sort)
176
+ #=> 895.413 (± 1.7%) i/s (1.12 ms/i) - 4.539k in 5.070590s
177
+ #=>
178
+ #=> Comparison:
179
+ #=> Array#bsearch (single sort): 895.4 i/s
180
+ #=> Array#find (single sort): 690.6 i/s - 1.30x slower
181
+ #=> RedBlackTree#search: 12.9 i/s - 69.27x slower
182
+ #=> Array#bsearch (gradual sort): 0.3 i/s - 3354.39x slower
183
+ #=> Array#find (gradual sort): 0.3 i/s - 3412.57x slower
184
+ ```
185
+
186
+ ## WIP Features
187
+
188
+ - `RedBlackTree#max`
189
+ - `RedBlackTree#height`
190
+ - `RedBlackTree#clear`
191
+
43
192
  ## Development
44
193
 
45
194
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the
@@ -14,9 +14,9 @@ class RedBlackTree
14
14
  # @return [RedBlackTree::Node, nil] the root node
15
15
  attr_reader :root
16
16
 
17
- # @private
18
17
  # @return [RedBlackTree::Node, nil] the left most node
19
18
  attr_reader :left_most_node
19
+ alias_method :min, :left_most_node
20
20
 
21
21
  def initialize
22
22
  @size = 0
@@ -41,8 +41,13 @@ class RedBlackTree
41
41
  #
42
42
  # @return [RedBlackTree::Node, nil] the removed node
43
43
  def shift
44
- node = @left_most_node
45
- delete! node if node
44
+ return unless @left_most_node
45
+
46
+ node = @left_most_node.dup
47
+ node.colour = node.parent = node.left = node.right = nil
48
+
49
+ delete! @left_most_node
50
+
46
51
  node
47
52
  end
48
53
 
@@ -72,8 +77,8 @@ class RedBlackTree
72
77
  #
73
78
  # @private ideally this is only used internally e.g. in #<< which has context on the ideal location for the node
74
79
  # @param node [RedBlackTree::Node] the node to be inserted
75
- # @param target_parent [RedBlackTree::Node, nil] the parent under which the node should be inserted
76
- # @param direction ["left", "right", nil] the direction of the node relative to the parent
80
+ # @param target_parent [RedBlackTree::Node] the parent under which the node should be inserted
81
+ # @param direction ["left", "right"] the direction of the node relative to the parent
77
82
  # @return [RedBlackTree] self
78
83
  def insert! node, target_parent = nil, direction = nil
79
84
  raise ArgumentError, "cannot insert leaf node" if node.instance_of? LeafNode
@@ -85,8 +90,6 @@ class RedBlackTree
85
90
  raise ArgumentError, "Target parent already has #{direction} child" if (child = target_parent[direction]) && child.valid?
86
91
  end
87
92
 
88
- opp_direction = opposite_direction direction if direction
89
-
90
93
  node.parent = nil
91
94
  node.left = LeafNode.new
92
95
  node.left.parent = node
@@ -108,17 +111,16 @@ class RedBlackTree
108
111
  node.parent.parent.red!
109
112
  node = node.parent.parent
110
113
  else
111
- if node.position == direction
112
- node = node.parent
113
- rotate_sub_tree! node, opp_direction
114
+ opp_direction = node.opposite_position
115
+ if node.parent.position == opp_direction
116
+ rotate_sub_tree! node.parent, opp_direction
117
+ node = node[opp_direction]
114
118
  end
115
119
 
120
+ opp_direction = node.opposite_position
121
+ rotate_sub_tree! node.parent.parent, opp_direction
116
122
  node.parent.black!
117
-
118
- if node.parent.parent
119
- node.parent.parent.red!
120
- rotate_sub_tree! node.parent.parent, direction
121
- end
123
+ node.parent[opp_direction].red!
122
124
  end
123
125
 
124
126
  @root.black!
@@ -140,6 +142,8 @@ class RedBlackTree
140
142
  def delete! node
141
143
  raise ArgumentError, "cannot delete leaf node" if node.instance_of? LeafNode
142
144
 
145
+ original_node = node
146
+
143
147
  if node.children_are_valid?
144
148
  successor = node.left
145
149
  successor = successor.left until successor.left.leaf?
@@ -160,19 +164,19 @@ class RedBlackTree
160
164
  if is_root? node
161
165
  @root = nil
162
166
  elsif node.red?
163
- leaf = LeafNode.new
164
- node.swap_position_with! leaf
167
+ node.swap_position_with! LeafNode.new
165
168
  else
166
- direction = node.position
167
- opp_direction = opposite_direction direction
168
-
169
169
  loop do
170
- if node.sibling.valid? && node.sibling.red?
170
+ if node.sibling && node.sibling.valid? && node.sibling.red?
171
171
  node.parent.red!
172
172
  node.sibling.black!
173
- rotate_sub_tree! node.parent, direction
173
+ rotate_sub_tree! node.parent, node.position
174
+ end
174
175
 
175
- next
176
+ if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
177
+ node.sibling.red! unless node.sibling.leaf?
178
+ node.close_nephew.black!
179
+ rotate_sub_tree! node.sibling, node.opposite_position
176
180
  end
177
181
 
178
182
  if node.distant_nephew && node.distant_nephew.valid? && node.distant_nephew.red?
@@ -182,35 +186,30 @@ class RedBlackTree
182
186
  end
183
187
  node.parent.black!
184
188
  node.distant_nephew.black!
185
- rotate_sub_tree! node.parent, direction
189
+ rotate_sub_tree! node.parent, node.position
186
190
 
187
191
  break
188
192
  end
189
193
 
190
- if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
191
- node.sibling.red!
192
- node.close_nephew.black!
193
- rotate_sub_tree! node.sibling, opp_direction
194
+ if node.parent && node.parent.red?
195
+ node.sibling.red! unless node.sibling.leaf?
196
+ node.parent.black!
194
197
 
195
- next
198
+ break
196
199
  end
197
200
 
198
- if node.parent.red?
201
+ if node.sibling && !node.sibling.leaf?
199
202
  node.sibling.red!
200
- node.parent.black!
201
-
202
- break
203
203
  end
204
204
 
205
- break
205
+ break unless node = node.parent
206
206
  end
207
207
 
208
- leaf = LeafNode.new
209
- node.swap_position_with! leaf
208
+ original_node.swap_position_with! LeafNode.new
210
209
  end
211
210
  end
212
211
 
213
- node.validate_free!
212
+ original_node.validate_free!
214
213
 
215
214
  decrement_size!
216
215
  update_left_most_node!
@@ -218,12 +217,90 @@ class RedBlackTree
218
217
  self
219
218
  end
220
219
 
221
- private
220
+ # Searches for a node which matches the given data/value.
221
+ #
222
+ # @param data [any] the data to search for
223
+ # @yield [RedBlackTree::Node] the block to be used for comparison
224
+ # @return [RedBlackTree::Node, nil] the matching node
225
+ def search data = nil, &block
226
+ if block_given?
227
+ raise ArgumentError, "provide either data or block, not both" if data
228
+
229
+ _search_by_block block, @root
230
+ else
231
+ raise ArgumentError, "data must be provided for search" unless data
222
232
 
223
- def is_root? node
224
- node.object_id == @root.object_id
233
+ _search_by_data data, @root
234
+ end
235
+ end
236
+
237
+ # Returns true if there is a node which matches the given data/value, and false if there is not.
238
+ #
239
+ # @return [true, false]
240
+ def include? data
241
+ !!search(data)
242
+ end
243
+
244
+ # Traverses the tree in pre-order and yields each node.
245
+ #
246
+ # @param node [RedBlackTree::Node] the node to start the traversal from
247
+ # @yield [RedBlackTree::Node] the block to be executed for each node
248
+ # @return [void]
249
+ def traverse_pre_order node = @root, &block
250
+ return if node.nil? || node.leaf?
251
+
252
+ block.call node
253
+ traverse_pre_order node.left, &block
254
+ traverse_pre_order node.right, &block
255
+ end
256
+
257
+ # Traverses the tree in in-order and yields each node.
258
+ #
259
+ # @param node [RedBlackTree::Node] the node to start the traversal from
260
+ # @yield [RedBlackTree::Node] the block to be executed for each node
261
+ # @return [void]
262
+ def traverse_in_order node = @root, &block
263
+ return if node.nil? || node.leaf?
264
+
265
+ traverse_in_order node.left, &block
266
+ block.call node
267
+ traverse_in_order node.right, &block
268
+ end
269
+ alias_method :traverse, :traverse_in_order
270
+
271
+ # Traverses the tree in post-order and yields each node.
272
+ #
273
+ # @param node [RedBlackTree::Node] the node to start the traversal from
274
+ # @yield [RedBlackTree::Node] the block to be executed for each node
275
+ # @return [void]
276
+ def traverse_post_order node = @root, &block
277
+ return if node.nil? || node.leaf?
278
+
279
+ traverse_post_order node.left, &block
280
+ traverse_post_order node.right, &block
281
+ block.call node
282
+ end
283
+
284
+ # Traverses the tree in level-order and yields each node.
285
+ #
286
+ # @yield [RedBlackTree::Node] the block to be executed for each node
287
+ # @return [void]
288
+ def traverse_level_order &block
289
+ return if @root.nil?
290
+
291
+ queue = [@root]
292
+ until queue.empty?
293
+ node = queue.shift
294
+ next if node.nil? || node.leaf?
295
+
296
+ block.call node
297
+ queue << node.left
298
+ queue << node.right
299
+ end
225
300
  end
226
301
 
302
+ private
303
+
227
304
  # Rotates a (sub-)tree starting from the given node in the given direction.
228
305
  #
229
306
  # @param node [RedBlackTree::Node] the root node of the sub-tree
@@ -250,6 +327,30 @@ class RedBlackTree
250
327
  opp_direction_child
251
328
  end
252
329
 
330
+ def _search_by_block block, node
331
+ traverse node do |current|
332
+ next if current.leaf?
333
+
334
+ return current if block.call current
335
+ end
336
+ end
337
+
338
+ def _search_by_data data, node
339
+ return if node.nil? || node.leaf?
340
+ return node if data == node.data
341
+
342
+ mock_node = node.class.new data
343
+ if mock_node >= node
344
+ _search_by_data data, node.right
345
+ else
346
+ _search_by_data data, node.left
347
+ end
348
+ end
349
+
350
+ def is_root? node
351
+ node && @root && node.object_id == @root.object_id
352
+ end
353
+
253
354
  def increment_size!
254
355
  @size += 1
255
356
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedBlackTree
4
+ module DataDelegation
5
+ def method_missing method_name, *args, &block
6
+ if @data.respond_to? method_name
7
+ @data.public_send method_name, *args, &block
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def respond_to_missing? method_name, include_private = false
14
+ @data.respond_to? method_name, include_private || super
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedBlackTree
4
+ module LeafNodeComparable # @private
5
+ def <=> other
6
+ if other.instance_of? ::RedBlackTree::LeafNode
7
+ 1
8
+ else
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
@@ -7,7 +7,7 @@ class RedBlackTree
7
7
  case direction
8
8
  when Node::LEFT then @left
9
9
  when Node::RIGHT then @right
10
- else raise ArgumentError, "Direction must be one of #{DIRECTIONS}"
10
+ else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}"
11
11
  end
12
12
  end
13
13
 
@@ -15,7 +15,7 @@ class RedBlackTree
15
15
  case direction
16
16
  when Node::LEFT then @left = node
17
17
  when Node::RIGHT then @right = node
18
- else raise ArgumentError, "Direction must be one of #{DIRECTIONS}"
18
+ else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}"
19
19
  end
20
20
  end
21
21
  end
@@ -1,11 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "utils"
4
+ require_relative "node/leaf_node_comparable"
5
+ require_relative "node/data_delegation"
4
6
  require_relative "node/left_right_element_referencers"
5
7
 
6
8
  class RedBlackTree
7
9
  class Node
10
+ class << self
11
+ def inherited subclass
12
+ subclass.prepend LeafNodeComparable
13
+ end
14
+ end
15
+
8
16
  include Comparable
17
+ include DataDelegation
9
18
 
10
19
  # @return [any] the data/value representing the node
11
20
  attr_reader :data
@@ -42,8 +51,8 @@ class RedBlackTree
42
51
  RIGHT = "right"
43
52
  DIRECTIONS = [LEFT, RIGHT].freeze
44
53
 
45
- attr_reader :colour
46
54
  attr_writer :data
55
+ attr_accessor :colour
47
56
  attr_accessor :tree, :parent
48
57
  attr_accessor :left, :right
49
58
  include LeftRightElementReferencers
@@ -65,9 +74,9 @@ class RedBlackTree
65
74
  def position
66
75
  return unless @parent
67
76
 
68
- case self
69
- when @parent.left then LEFT
70
- when @parent.right then RIGHT
77
+ case self.object_id
78
+ when @parent.left.object_id then LEFT
79
+ when @parent.right.object_id then RIGHT
71
80
  else raise StructuralError, "Disowned by parent"
72
81
  end
73
82
  end
@@ -140,13 +149,13 @@ class RedBlackTree
140
149
  self_position = position
141
150
  other_position = other_node.position
142
151
 
143
- if other_node.parent == self
152
+ if other_node.parent.object_id == self.object_id
144
153
  self[other_position] = other_node[other_position]
145
154
  other_node[other_position] = self
146
155
 
147
156
  other_node.parent = @parent
148
157
  @parent = other_node
149
- elsif other_node == @parent
158
+ elsif other_node.object_id == @parent.object_id
150
159
  other_node[self_position] = self[self_position]
151
160
  self[self_position] = other_node
152
161
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedBlackTree
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: red-black-tree
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
  - Joshua Young
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-03 00:00:00.000000000 Z
11
+ date: 2024-10-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -23,7 +23,9 @@ files:
23
23
  - README.md
24
24
  - lib/red-black-tree.rb
25
25
  - lib/red_black_tree/node.rb
26
+ - lib/red_black_tree/node/data_delegation.rb
26
27
  - lib/red_black_tree/node/leaf_node.rb
28
+ - lib/red_black_tree/node/leaf_node_comparable.rb
27
29
  - lib/red_black_tree/node/left_right_element_referencers.rb
28
30
  - lib/red_black_tree/utils.rb
29
31
  - lib/red_black_tree/version.rb
@@ -50,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
52
  - !ruby/object:Gem::Version
51
53
  version: '0'
52
54
  requirements: []
53
- rubygems_version: 3.5.15
55
+ rubygems_version: 3.5.16
54
56
  signing_key:
55
57
  specification_version: 4
56
58
  summary: Red-Black Tree Data Structure for Ruby