red-black-tree 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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