red-black-tree 0.1.5 → 0.1.7

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: b90594d4e04c47851ed0eaa18f0f517c37081a9c52c21ae5d4af66b142e2fdb1
4
- data.tar.gz: de26391d21001786ee4f39859ef248985bbb5ca9750187f069219b3fdd72813e
3
+ metadata.gz: 291198bed7dd82b504c47b1c6fa047674c9302a7c529d33409d61797a0677929
4
+ data.tar.gz: ab8b8d513625de270d0126c0c3f1d5d787260b32f1bd0f13927332e9e2640aab
5
5
  SHA512:
6
- metadata.gz: df049ff727c8918d99e255e44977281e378269620d04dbb16e3d6d1990e72416d85c1c42c7815de67d0c2f1366c68db463587d4cdfe56213cc6f45e4476f6648
7
- data.tar.gz: dea0e150ee92555bff5af6e81cf81e4df9f75326a3c7e8bfa907c24b17dfa42f13548f113d9ec0b3448127a92794a0059701136eda88cd7c6fe0c44f5a75bfe9
6
+ metadata.gz: aed7c4bd38468df481ebf815605f8564bc66ecfb8b7a53ee5babefaad2f68fc0a08994a9e91922f161f628a23d63eff02e57c7418bd7ce80b6db4f95723aa421
7
+ data.tar.gz: 2ec1c08f09e3ef1ca7bf608032cfb4a28042e57ec357dd40ee1c8951c64e0c7d944f7900db4bf9acc758513070f5533745e4b6a3fa4477567644de73cc62787c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.7] - 2025-01-05
4
+
5
+ - Change `RedBlackTree#delete!` return value to the deleted node
6
+ - Add `RedBlackTree#clear!`
7
+
8
+ ## [0.1.6] - 2025-01-01
9
+
10
+ - Prefer node deletion over data swapping
11
+
3
12
  ## [0.1.5] - 2024-10-28
4
13
 
5
14
  - Alias `RedBlackTree#search` as `RedBlackTree#find`
data/README.md CHANGED
@@ -83,20 +83,20 @@ Benchmark.ips do |x|
83
83
  x.compare!
84
84
  end
85
85
 
86
- #=> ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin24]
86
+ #=> ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24]
87
87
  #=> Warming up --------------------------------------
88
88
  #=> RedBlackTree 1.000 i/100ms
89
89
  #=> Array (gradual sort) 1.000 i/100ms
90
- #=> Array (single sort) 78.000 i/100ms
90
+ #=> Array (single sort) 100.000 i/100ms
91
91
  #=> Calculating -------------------------------------
92
- #=> RedBlackTree 5.4170.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
92
+ #=> RedBlackTree 16.6846.0%) i/s (59.94 ms/i) - 84.000 in 5.053688s
93
+ #=> Array (gradual sort) 0.270 (± 0.0%) i/s (3.71 s/i) - 2.000 in 7.418650s
94
+ #=> Array (single sort) 1.006k (± 2.4%) i/s (994.28 μs/i) - 5.100k in 5.073834s
95
95
  #=>
96
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
97
+ #=> Array (single sort): 1005.8 i/s
98
+ #=> RedBlackTree: 16.7 i/s - 60.28x slower
99
+ #=> Array (gradual sort): 0.3 i/s - 3729.44x slower
100
100
  ```
101
101
 
102
102
  ### Sort and search 10,000 elements
@@ -153,41 +153,40 @@ Benchmark.ips do |x|
153
153
  x.compare!
154
154
  end
155
155
 
156
- #=> ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin24]
156
+ #=> ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24]
157
157
  #=> Warming up --------------------------------------
158
- #=> RedBlackTree#search 1.000 i/100ms
158
+ #=> RedBlackTree#search 3.000 i/100ms
159
159
  #=> Array#find (gradual sort)
160
160
  #=> 1.000 i/100ms
161
161
  #=> Array#find (single sort)
162
- #=> 69.000 i/100ms
162
+ #=> 95.000 i/100ms
163
163
  #=> Array#bsearch (gradual sort)
164
164
  #=> 1.000 i/100ms
165
165
  #=> Array#bsearch (single sort)
166
- #=> 89.000 i/100ms
166
+ #=> 108.000 i/100ms
167
167
  #=> Calculating -------------------------------------
168
- #=> RedBlackTree#search 12.9260.0%) i/s (77.36 ms/i) - 65.000 in 5.030736s
168
+ #=> RedBlackTree#search 42.8504.7%) i/s (23.34 ms/i) - 216.000 in 5.049896s
169
169
  #=> Array#find (gradual sort)
170
- #=> 0.262 (± 0.0%) i/s (3.81 s/i) - 2.000 in 7.623953s
170
+ #=> 0.274 (± 0.0%) i/s (3.65 s/i) - 2.000 in 7.302412s
171
171
  #=> Array#find (single sort)
172
- #=> 690.6311.0%) i/s (1.45 ms/i) - 3.519k in 5.095823s
172
+ #=> 935.4922.7%) i/s (1.07 ms/i) - 4.750k in 5.081415s
173
173
  #=> Array#bsearch (gradual sort)
174
- #=> 0.267 (± 0.0%) i/s (3.75 s/i) - 2.000 in 7.492482s
174
+ #=> 0.275 (± 0.0%) i/s (3.63 s/i) - 2.000 in 7.269027s
175
175
  #=> Array#bsearch (single sort)
176
- #=> 895.4131.7%) i/s (1.12 ms/i) - 4.539k in 5.070590s
176
+ #=> 1.104k2.0%) i/s (905.52 μs/i) - 5.616k in 5.087395s
177
177
  #=>
178
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
179
+ #=> Array#bsearch (single sort): 1104.3 i/s
180
+ #=> Array#find (single sort): 935.5 i/s - 1.18x slower
181
+ #=> RedBlackTree#search: 42.9 i/s - 25.77x slower
182
+ #=> Array#bsearch (gradual sort): 0.3 i/s - 4013.67x slower
183
+ #=> Array#find (gradual sort): 0.3 i/s - 4031.85x slower
184
184
  ```
185
185
 
186
186
  ## WIP Features
187
187
 
188
188
  - `RedBlackTree#max`
189
189
  - `RedBlackTree#height`
190
- - `RedBlackTree#clear`
191
190
 
192
191
  ## Development
193
192
 
@@ -20,6 +20,7 @@ class RedBlackTree
20
20
 
21
21
  def initialize
22
22
  @size = 0
23
+ @root = nil
23
24
  @left_most_node = nil
24
25
  end
25
26
 
@@ -90,6 +91,7 @@ class RedBlackTree
90
91
  raise ArgumentError, "Target parent already has #{direction} child" if (child = target_parent[direction]) && child.valid?
91
92
  end
92
93
 
94
+ node.tree = self
93
95
  node.parent = nil
94
96
  node.left = LeafNode.new
95
97
  node.left.parent = node
@@ -138,28 +140,37 @@ class RedBlackTree
138
140
  # Removes the given node from the tree.
139
141
  #
140
142
  # @param node [RedBlackTree::Node] the node to be removed
141
- # @return [RedBlackTree] self
143
+ # @return [RedBlackTree::Node, nil] the removed node
142
144
  def delete! node
143
145
  raise ArgumentError, "cannot delete leaf node" if node.instance_of? LeafNode
144
146
 
147
+ if node.tree.nil?
148
+ return
149
+ elsif node.tree != self
150
+ raise ArgumentError, "node does not belong to this tree"
151
+ end
152
+
145
153
  original_node = node
146
154
 
147
155
  if node.children_are_valid?
156
+ is_root = is_root? node
157
+
148
158
  successor = node.left
149
159
  successor = successor.left until successor.left.leaf?
150
- node.swap_data_with! successor
160
+ node.swap_colour_with! successor
161
+ node.swap_position_with! successor
162
+ node.swap_position_with! LeafNode.new
151
163
 
152
- return delete! successor
164
+ @root = successor if is_root
153
165
  elsif node.single_child_is_valid?
154
166
  is_root = is_root? node
155
167
 
156
168
  valid_child = node.children.find(&:valid?)
157
- node.swap_data_with! valid_child
158
- node.black!
159
-
160
- @root = node if is_root
169
+ valid_child.black!
170
+ node.swap_position_with! valid_child
171
+ node.swap_position_with! LeafNode.new
161
172
 
162
- return delete! valid_child
173
+ @root = valid_child if is_root
163
174
  elsif node.children_are_leaves?
164
175
  if is_root? node
165
176
  @root = nil
@@ -209,11 +220,23 @@ class RedBlackTree
209
220
  end
210
221
  end
211
222
 
223
+ original_node.tree = nil
212
224
  original_node.validate_free!
213
225
 
214
226
  decrement_size!
215
227
  update_left_most_node!
216
228
 
229
+ original_node
230
+ end
231
+
232
+ # Removes all nodes from the tree.
233
+ #
234
+ # @return [RedBlackTree] self
235
+ def clear!
236
+ @root = nil
237
+ @size = 0
238
+ @left_most_node = nil
239
+
217
240
  self
218
241
  end
219
242
 
@@ -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 #{Implementation::DIRECTIONS}"
10
+ else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}, got #{direction}"
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 #{Implementation::DIRECTIONS}"
18
+ else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}, got #{direction}"
19
19
  end
20
20
  end
21
21
  end
@@ -19,6 +19,9 @@ class RedBlackTree
19
19
  # @return [any] the data/value representing the node
20
20
  attr_reader :data
21
21
 
22
+ # @return [RedBlackTree::Node, nil] the tree this node belongs to
23
+ attr_reader :tree
24
+
22
25
  # @param data [any] a non-nil data/value representing the node
23
26
  # @return [Node] a new instance of Node
24
27
  def initialize data
@@ -51,10 +54,8 @@ class RedBlackTree
51
54
  RIGHT = "right"
52
55
  DIRECTIONS = [LEFT, RIGHT].freeze
53
56
 
54
- attr_writer :data
55
- attr_accessor :colour
56
- attr_accessor :tree, :parent
57
- attr_accessor :left, :right
57
+ attr_writer :data, :tree
58
+ attr_accessor :colour, :parent, :left, :right
58
59
  include LeftRightElementReferencers
59
60
 
60
61
  def red? = @colour == RED
@@ -116,6 +117,8 @@ class RedBlackTree
116
117
  end
117
118
 
118
119
  def validate_free!
120
+ raise StructuralError, "Node is still chained to a tree" if @tree
121
+
119
122
  anchors = []
120
123
  anchors << "parent" if @parent
121
124
  anchors << "left child" if @left && @left.valid?
@@ -125,12 +128,6 @@ class RedBlackTree
125
128
  raise StructuralError, "Node is still chained to #{anchors.join(", ")}"
126
129
  end
127
130
 
128
- def swap_data_with! other_node
129
- temp_data = @data
130
- @data = other_node.data
131
- other_node.data = temp_data
132
- end
133
-
134
131
  def swap_colour_with! other_node
135
132
  temp_colour = @colour
136
133
 
@@ -148,27 +145,47 @@ class RedBlackTree
148
145
  def swap_position_with! other_node
149
146
  self_position = position
150
147
  other_position = other_node.position
148
+ opp_other_position = opposite_direction other_position if other_position
151
149
 
152
150
  if other_node.parent.object_id == self.object_id
153
151
  self[other_position] = other_node[other_position]
154
152
  other_node[other_position] = self
155
153
 
154
+ self[other_position].parent = self
156
155
  other_node.parent = @parent
156
+
157
+ @parent[self_position] = other_node if self_position
157
158
  @parent = other_node
158
- elsif other_node.object_id == @parent.object_id
159
- other_node[self_position] = self[self_position]
160
- self[self_position] = other_node
161
159
 
162
- temp_parent = other_node.parent
163
- other_node.parent = self
164
- @parent = temp_parent
160
+ temp_node = self[opp_other_position]
161
+ self[opp_other_position] = other_node[opp_other_position]
162
+ other_node[opp_other_position] = temp_node
163
+
164
+ self[opp_other_position].parent = self
165
+ other_node[opp_other_position].parent = other_node
166
+ elsif other_node.object_id == @parent.object_id
167
+ other_node.swap_position_with! self
165
168
  else
166
169
  other_node.parent[other_position] = self if other_node.parent
167
170
  @parent[self_position] = other_node if @parent
168
171
 
169
- temp_parent = other_node.parent
172
+ temp_node = other_node.parent
170
173
  other_node.parent = @parent
171
- @parent = temp_parent
174
+ @parent = temp_node
175
+
176
+ temp_node = other_node.left
177
+ other_node.left = @left
178
+ @left = temp_node
179
+
180
+ @left.parent = self if @left
181
+ other_node.left.parent = other_node if other_node.left
182
+
183
+ temp_node = other_node.right
184
+ other_node.right = @right
185
+ @right = temp_node
186
+
187
+ @right.parent = self if @right
188
+ other_node.right.parent = other_node if other_node.right
172
189
  end
173
190
  end
174
191
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedBlackTree
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
metadata CHANGED
@@ -1,16 +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.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-27 00:00:00.000000000 Z
10
+ date: 2025-01-05 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - djry1999@gmail.com
16
14
  executables: []
@@ -37,7 +35,6 @@ metadata:
37
35
  documentation_uri: https://joshuay03.github.io/red-black-tree/
38
36
  source_code_uri: https://github.com/joshuay03/red-black-tree
39
37
  changelog_uri: https://github.com/joshuay03/red-black-tree/blob/main/CHANGELOG.md
40
- post_install_message:
41
38
  rdoc_options: []
42
39
  require_paths:
43
40
  - lib
@@ -52,8 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
49
  - !ruby/object:Gem::Version
53
50
  version: '0'
54
51
  requirements: []
55
- rubygems_version: 3.5.16
56
- signing_key:
52
+ rubygems_version: 3.6.2
57
53
  specification_version: 4
58
54
  summary: Red-Black Tree Data Structure for Ruby
59
55
  test_files: []