red-black-tree 0.1.8 → 0.1.10

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: e7cc7e3138c1b0ceea2d6b5a2f91620323f75ebe346288bc69761d77d95eb5c4
4
- data.tar.gz: 057d9d283d5312f6c5f8e155e3c039c342d036b779d115738c7afbc57ca419f4
3
+ metadata.gz: ef532dd5b5da89ad3dbf780bba52824ecdece998306dc89d4742ddd7b9444a1b
4
+ data.tar.gz: dee8cd6cb0ebf9b837519d9131790590e6fcab86c5fe45af0d28cb603d53f224
5
5
  SHA512:
6
- metadata.gz: 61f4a9a44d3949b3c2072de4fbca1507cf78306adc32dae8bad26ec5a71171ae952cb81effddf41990a8e2b6dc0e06d39a121389e5bc4c3438eca981dc2a0b59
7
- data.tar.gz: c9bced3565a3ec9439c1c08dca09676d1c3966cb18a97507c1f815e938b73419f9f3bce0e4d13d06e97dc59a92f845a3b9b4f094841aa900bde3b004959baeca
6
+ metadata.gz: 75b0530cb685fb3cfcd1fa8435a8329e97df9fbd49e5e7063b5de0c34cbef24c453dc5d22481c8ba4cafe475f48ea3ccafa37788ef92b066c667882ce860130c
7
+ data.tar.gz: 8fdf009768bfb1551f2f4954ddab323c12b1d76d438c0be8b2bbb4ffe10816de58513e2642649ad78b25b1ea931a03a29a82b3d2df6a3dfeae70997158fe9183
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.10] - 2026-05-17
4
+
5
+ - Fix `RedBlackTree#delete!` orphaning predecessor's child when deleting a node with two valid children
6
+
7
+ ## [0.1.9] - 2026-05-10
8
+
9
+ - Add `Enumerable` to `RedBlackTree`
10
+ - Fix `RedBlackTree#clear!` not detaching nodes
11
+ - Fix `LeafNodeComparable` being prepended to `LeafNode`
12
+
3
13
  ## [0.1.8] - 2025-07-26
4
14
 
5
15
  - Refactor to slim down and de-duplicate some methods
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # RedBlackTree
2
2
 
3
3
  ![Version](https://img.shields.io/gem/v/red-black-tree)
4
- ![Build](https://img.shields.io/github/actions/workflow/status/joshuay03/red-black-tree/.github/workflows/tests.yml?branch=main)
4
+ ![Build](https://badge.buildkite.com/6ccbd68c23960899d1deafdb4cfaa96f1e8c04ad6e198e193b.svg)
5
5
 
6
6
  [Red-black tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) data structure for Ruby.
7
7
 
@@ -86,20 +86,20 @@ Benchmark.ips do |x|
86
86
  x.compare!
87
87
  end
88
88
 
89
- #=> ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24]
89
+ #=> ruby 4.0.3 (2026-04-21 revision 85ddef263a) +YJIT +PRISM [arm64-darwin23]
90
90
  #=> Warming up --------------------------------------
91
91
  #=> RedBlackTree 1.000 i/100ms
92
92
  #=> Array (gradual sort) 1.000 i/100ms
93
- #=> Array (single sort) 100.000 i/100ms
93
+ #=> Array (single sort) 81.000 i/100ms
94
94
  #=> Calculating -------------------------------------
95
- #=> RedBlackTree 16.6846.0%) i/s (59.94 ms/i) - 84.000 in 5.053688s
96
- #=> Array (gradual sort) 0.270 (± 0.0%) i/s (3.71 s/i) - 2.000 in 7.418650s
97
- #=> Array (single sort) 1.006k (± 2.4%) i/s (994.28 μs/i) - 5.100k in 5.073834s
98
- #=>
95
+ #=> RedBlackTree 12.1708.2%) i/s (82.17 ms/i) - 61.000 in 5.023274s
96
+ #=> Array (gradual sort) 0.208 (± 0.0%) i/s (4.82 s/i) - 2.000 in 9.632733s
97
+ #=> Array (single sort) 815.7621.2%) i/s (1.23 ms/i) - 4.131k in 5.064788s
98
+ #=>
99
99
  #=> Comparison:
100
- #=> Array (single sort): 1005.8 i/s
101
- #=> RedBlackTree: 16.7 i/s - 60.28x slower
102
- #=> Array (gradual sort): 0.3 i/s - 3729.44x slower
100
+ #=> Array (single sort): 815.8 i/s
101
+ #=> RedBlackTree: 12.2 i/s - 67.03x slower
102
+ #=> Array (gradual sort): 0.2 i/s - 3928.64x slower
103
103
  ```
104
104
 
105
105
  ### Sort and search 10,000 elements
@@ -122,7 +122,7 @@ Benchmark.ips do |x|
122
122
  x.report("RedBlackTree#search") do
123
123
  tree = RedBlackTree.new
124
124
  sample_data.each { |work| tree << WorkNode.new(work); }
125
- raise unless tree.search search_sample
125
+ raise unless tree.search { |node| node.data == search_sample }
126
126
  end
127
127
 
128
128
  # 1:1 comparison
@@ -156,34 +156,34 @@ Benchmark.ips do |x|
156
156
  x.compare!
157
157
  end
158
158
 
159
- #=> ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24]
159
+ #=> ruby 4.0.3 (2026-04-21 revision 85ddef263a) +YJIT +PRISM [arm64-darwin23]
160
160
  #=> Warming up --------------------------------------
161
161
  #=> RedBlackTree#search 3.000 i/100ms
162
162
  #=> Array#find (gradual sort)
163
163
  #=> 1.000 i/100ms
164
164
  #=> Array#find (single sort)
165
- #=> 95.000 i/100ms
165
+ #=> 79.000 i/100ms
166
166
  #=> Array#bsearch (gradual sort)
167
167
  #=> 1.000 i/100ms
168
168
  #=> Array#bsearch (single sort)
169
- #=> 108.000 i/100ms
169
+ #=> 87.000 i/100ms
170
170
  #=> Calculating -------------------------------------
171
- #=> RedBlackTree#search 42.8504.7%) i/s (23.34 ms/i) - 216.000 in 5.049896s
171
+ #=> RedBlackTree#search 32.1886.2%) i/s (31.07 ms/i) - 162.000 in 5.051229s
172
172
  #=> Array#find (gradual sort)
173
- #=> 0.274 (± 0.0%) i/s (3.65 s/i) - 2.000 in 7.302412s
173
+ #=> 0.207 (± 0.0%) i/s (4.83 s/i) - 2.000 in 9.651532s
174
174
  #=> Array#find (single sort)
175
- #=> 935.4922.7%) i/s (1.07 ms/i) - 4.750k in 5.081415s
175
+ #=> 809.3103.8%) i/s (1.24 ms/i) - 4.108k in 5.084020s
176
176
  #=> Array#bsearch (gradual sort)
177
- #=> 0.275 (± 0.0%) i/s (3.63 s/i) - 2.000 in 7.269027s
177
+ #=> 0.205 (± 0.0%) i/s (4.88 s/i) - 2.000 in 9.753754s
178
178
  #=> Array#bsearch (single sort)
179
- #=> 1.104k2.0%) i/s (905.52 μs/i) - 5.616k in 5.087395s
180
- #=>
179
+ #=> 857.4971.0%) i/s (1.17 ms/i) - 4.350k in 5.073414s
180
+ #=>
181
181
  #=> Comparison:
182
- #=> Array#bsearch (single sort): 1104.3 i/s
183
- #=> Array#find (single sort): 935.5 i/s - 1.18x slower
184
- #=> RedBlackTree#search: 42.9 i/s - 25.77x slower
185
- #=> Array#bsearch (gradual sort): 0.3 i/s - 4013.67x slower
186
- #=> Array#find (gradual sort): 0.3 i/s - 4031.85x slower
182
+ #=> Array#bsearch (single sort): 857.5 i/s
183
+ #=> Array#find (single sort): 809.3 i/s - 1.06x slower
184
+ #=> RedBlackTree#search: 32.2 i/s - 26.64x slower
185
+ #=> Array#find (gradual sort): 0.2 i/s - 4138.00x slower
186
+ #=> Array#bsearch (gradual sort): 0.2 i/s - 4181.79x slower
187
187
  ```
188
188
 
189
189
  ## WIP Features
@@ -6,6 +6,7 @@ require_relative "red_black_tree/node/leaf_node"
6
6
 
7
7
  class RedBlackTree
8
8
  include Utils
9
+ include Enumerable
9
10
 
10
11
  # @return [Integer] the number of valid/non-leaf nodes
11
12
  attr_reader :size
@@ -134,7 +135,9 @@ class RedBlackTree
134
135
 
135
136
  if node.children_are_valid?
136
137
  delete_node_with_two_children! node
137
- elsif node.single_child_is_valid?
138
+ end
139
+
140
+ if node.single_child_is_valid?
138
141
  delete_node_with_one_child! node
139
142
  elsif node.children_are_leaves?
140
143
  delete_leaf_node! node, original_node
@@ -153,6 +156,11 @@ class RedBlackTree
153
156
  #
154
157
  # @return [RedBlackTree] self
155
158
  def clear!
159
+ traverse_post_order do |node|
160
+ node.tree = nil
161
+ node.colour = node.parent = node.left = node.right = nil
162
+ end
163
+
156
164
  @root = nil
157
165
  @size = 0
158
166
  @left_most_node = nil
@@ -179,7 +187,7 @@ class RedBlackTree
179
187
  alias_method :find, :search
180
188
 
181
189
  def select &block
182
- raise ArgumentError, "block must be provided for select" unless block
190
+ raise ArgumentError, "block must be provided for select" unless block_given?
183
191
 
184
192
  _select_by_block block, @root
185
193
  end
@@ -193,6 +201,17 @@ class RedBlackTree
193
201
  !search(data).nil?
194
202
  end
195
203
 
204
+ # Traverses the tree in in-order and yields each node.
205
+ # When called without a block, returns an {Enumerator}.
206
+ #
207
+ # @yield [RedBlackTree::Node] the block to be executed for each node
208
+ # @return [void, Enumerator]
209
+ def each &block
210
+ return enum_for(:each) unless block_given?
211
+
212
+ traverse_in_order(&block)
213
+ end
214
+
196
215
  # Traverses the tree in pre-order and yields each node.
197
216
  #
198
217
  # @param node [RedBlackTree::Node] the node to start the traversal from
@@ -243,11 +262,9 @@ class RedBlackTree
243
262
  queue = [@root]
244
263
  until queue.empty?
245
264
  node = queue.shift
246
- next if node.nil? || node.leaf?
247
-
248
265
  block.call node
249
- queue << node.left
250
- queue << node.right
266
+ queue << node.left unless node.left.leaf?
267
+ queue << node.right unless node.right.leaf?
251
268
  end
252
269
  end
253
270
 
@@ -329,13 +346,12 @@ class RedBlackTree
329
346
  def delete_node_with_two_children! node
330
347
  is_root = is_root? node
331
348
 
332
- successor = node.left
333
- successor = successor.right until successor.right.leaf?
334
- node.swap_colour_with! successor
335
- node.swap_position_with! successor
336
- node.swap_position_with! LeafNode.new
349
+ predecessor = node.left
350
+ predecessor = predecessor.right until predecessor.right.leaf?
351
+ node.swap_colour_with! predecessor
352
+ node.swap_position_with! predecessor
337
353
 
338
- @root = successor if is_root
354
+ @root = predecessor if is_root
339
355
  end
340
356
 
341
357
  def delete_node_with_one_child! node
@@ -9,7 +9,7 @@ class RedBlackTree
9
9
  class Node
10
10
  class << self
11
11
  def inherited subclass
12
- subclass.prepend LeafNodeComparable
12
+ subclass.prepend LeafNodeComparable unless subclass == ::RedBlackTree::LeafNode
13
13
  end
14
14
  end
15
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedBlackTree
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.10"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: red-black-tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young
@@ -49,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0'
51
51
  requirements: []
52
- rubygems_version: 3.6.9
52
+ rubygems_version: 4.0.10
53
53
  specification_version: 4
54
54
  summary: Red-Black Tree Data Structure for Ruby
55
55
  test_files: []