red-black-tree 0.1.0 → 0.1.2

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: 4f43951dbc99839bcb925622284650c9f5ec3dbe3fa709706fac32345bbe1607
4
- data.tar.gz: 8d1d5cdc4420b9e29012ed4a16054f0bede9b9239327605151f97efc123d0636
3
+ metadata.gz: e609f03254a0ff34b07adf5445e91803cf4f50ffc5ba91b6288c31003218b87a
4
+ data.tar.gz: e1289ab4cefa28f5ae2eca05be45d5a7dbd2fa13a8a5256c81ac21511bb8e0fb
5
5
  SHA512:
6
- metadata.gz: b3f59b93fc6d1f5e10081f5f72d9c8346c4e117badc7f3ac5d694bac07308c511fc3ed71cb4b250c5d790fd6d77f13b219fd1d9accbd7120783147862ce52045
7
- data.tar.gz: 42d421b0ab688c03ea073ef34d460ed42cb3de4f14b5aa13ec4f243bf71fbc65533da0a2a7ee390ea33fc5828549bc62f514f3a592c0f42d179f3d4a693c6ab2
6
+ metadata.gz: 514eee78de1f75f5caaa55d285109d5e64a431a95732b3e6cabbee23e523ddd93b8b1e5e996c4ebcebce86e0ac54a15e0b88299a295515ce592254706f905e1a
7
+ data.tar.gz: '092aaa352db1d9d8d1082393bfff927c35dbb349ffc980720835379c27297f7d47f817edb2eb29a22103bc4beda53c683150b05756444d75b823e673161358e1'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.2] - 2024-09-08
4
+
5
+ - Fix a bunch of issues in `RedBlackTree#insert!` and `RedBlackTree#delete!` algorithms
6
+ - Fix `RedBlackTree::LeafNode`s being marked red
7
+ - Handle comparison with `RedBlackTree::LeafNode` in subclasses of `RedBlackTree::Node`
8
+ - Add `RedBlackTree#include?`
9
+ - Add `RedBlackTree#search`
10
+ - Alias `RedBlackTree#left_most_node` as `RedBlackTree#min`
11
+
12
+ ## [0.1.1] - 2024-08-04
13
+
14
+ - Update `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` in README
15
+
3
16
  ## [0.1.0] - 2024-08-04
4
17
 
5
18
  - Initial release
data/README.md CHANGED
@@ -6,31 +6,28 @@
6
6
 
7
7
  Install the gem and add to the application's Gemfile by executing:
8
8
 
9
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
9
+ $ bundle add red-black-tree
10
10
 
11
11
  If bundler is not being used to manage dependencies, install the gem by executing:
12
12
 
13
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
13
+ $ gem install red-black-tree
14
14
 
15
15
  ## Usage
16
16
 
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,17 @@ until tree.empty?
40
37
  end
41
38
  ```
42
39
 
40
+ ## WIP Features
41
+
42
+ - `RedBlackTree#traverse_in_order`
43
+ - `RedBlackTree#traverse_pre_order`
44
+ - `RedBlackTree#traverse_post_order`
45
+ - `RedBlackTree#traverse_level_order`
46
+ - `RedBlackTree#traverse` (default in-order)
47
+ - `RedBlackTree#max`
48
+ - `RedBlackTree#height`
49
+ - `RedBlackTree#clear`
50
+
43
51
  ## Development
44
52
 
45
53
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the
@@ -17,6 +17,7 @@ class RedBlackTree
17
17
  # @private
18
18
  # @return [RedBlackTree::Node, nil] the left most node
19
19
  attr_reader :left_most_node
20
+ alias_method :min, :left_most_node
20
21
 
21
22
  def initialize
22
23
  @size = 0
@@ -41,8 +42,13 @@ class RedBlackTree
41
42
  #
42
43
  # @return [RedBlackTree::Node, nil] the removed node
43
44
  def shift
44
- node = @left_most_node
45
- delete! node if node
45
+ return unless @left_most_node
46
+
47
+ node = @left_most_node.dup
48
+ node.colour = node.parent = node.left = node.right = nil
49
+
50
+ delete! @left_most_node
51
+
46
52
  node
47
53
  end
48
54
 
@@ -85,8 +91,6 @@ class RedBlackTree
85
91
  raise ArgumentError, "Target parent already has #{direction} child" if (child = target_parent[direction]) && child.valid?
86
92
  end
87
93
 
88
- opp_direction = opposite_direction direction if direction
89
-
90
94
  node.parent = nil
91
95
  node.left = LeafNode.new
92
96
  node.left.parent = node
@@ -108,17 +112,16 @@ class RedBlackTree
108
112
  node.parent.parent.red!
109
113
  node = node.parent.parent
110
114
  else
111
- if node.position == direction
112
- node = node.parent
113
- rotate_sub_tree! node, opp_direction
115
+ opp_direction = node.opposite_position
116
+ if node.parent.position == opp_direction
117
+ rotate_sub_tree! node.parent, opp_direction
118
+ node = node[opp_direction]
114
119
  end
115
120
 
121
+ opp_direction = node.opposite_position
122
+ rotate_sub_tree! node.parent.parent, opp_direction
116
123
  node.parent.black!
117
-
118
- if node.parent.parent
119
- node.parent.parent.red!
120
- rotate_sub_tree! node.parent.parent, direction
121
- end
124
+ node.parent[opp_direction].red!
122
125
  end
123
126
 
124
127
  @root.black!
@@ -140,6 +143,8 @@ class RedBlackTree
140
143
  def delete! node
141
144
  raise ArgumentError, "cannot delete leaf node" if node.instance_of? LeafNode
142
145
 
146
+ original_node = node
147
+
143
148
  if node.children_are_valid?
144
149
  successor = node.left
145
150
  successor = successor.left until successor.left.leaf?
@@ -160,19 +165,19 @@ class RedBlackTree
160
165
  if is_root? node
161
166
  @root = nil
162
167
  elsif node.red?
163
- leaf = LeafNode.new
164
- node.swap_position_with! leaf
168
+ node.swap_position_with! LeafNode.new
165
169
  else
166
- direction = node.position
167
- opp_direction = opposite_direction direction
168
-
169
170
  loop do
170
- if node.sibling.valid? && node.sibling.red?
171
+ if node.sibling && node.sibling.valid? && node.sibling.red?
171
172
  node.parent.red!
172
173
  node.sibling.black!
173
- rotate_sub_tree! node.parent, direction
174
+ rotate_sub_tree! node.parent, node.position
175
+ end
174
176
 
175
- next
177
+ if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
178
+ node.sibling.red! unless node.sibling.leaf?
179
+ node.close_nephew.black!
180
+ rotate_sub_tree! node.sibling, node.opposite_position
176
181
  end
177
182
 
178
183
  if node.distant_nephew && node.distant_nephew.valid? && node.distant_nephew.red?
@@ -182,35 +187,30 @@ class RedBlackTree
182
187
  end
183
188
  node.parent.black!
184
189
  node.distant_nephew.black!
185
- rotate_sub_tree! node.parent, direction
190
+ rotate_sub_tree! node.parent, node.position
186
191
 
187
192
  break
188
193
  end
189
194
 
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
195
+ if node.parent && node.parent.red?
196
+ node.sibling.red! unless node.sibling.leaf?
197
+ node.parent.black!
194
198
 
195
- next
199
+ break
196
200
  end
197
201
 
198
- if node.parent.red?
202
+ if node.sibling && !node.sibling.leaf?
199
203
  node.sibling.red!
200
- node.parent.black!
201
-
202
- break
203
204
  end
204
205
 
205
- break
206
+ break unless node = node.parent
206
207
  end
207
208
 
208
- leaf = LeafNode.new
209
- node.swap_position_with! leaf
209
+ original_node.swap_position_with! LeafNode.new
210
210
  end
211
211
  end
212
212
 
213
- node.validate_free!
213
+ original_node.validate_free!
214
214
 
215
215
  decrement_size!
216
216
  update_left_most_node!
@@ -218,12 +218,25 @@ class RedBlackTree
218
218
  self
219
219
  end
220
220
 
221
- private
221
+ # Searches for a node which matches the given data/value.
222
+ #
223
+ # @param data [any] the data to search for
224
+ # @return [RedBlackTree::Node, nil] the matching node
225
+ def search data
226
+ raise ArgumentError, "data must be provided for search" unless data
222
227
 
223
- def is_root? node
224
- node.object_id == @root.object_id
228
+ _search data, @root
225
229
  end
226
230
 
231
+ # Returns true if there is a node which matches the given data/value, and false if there is not.
232
+ #
233
+ # @return [true, false]
234
+ def include? data
235
+ !!search(data)
236
+ end
237
+
238
+ private
239
+
227
240
  # Rotates a (sub-)tree starting from the given node in the given direction.
228
241
  #
229
242
  # @param node [RedBlackTree::Node] the root node of the sub-tree
@@ -250,6 +263,22 @@ class RedBlackTree
250
263
  opp_direction_child
251
264
  end
252
265
 
266
+ def _search data, current_node
267
+ return if current_node.nil? || current_node.leaf?
268
+ return current_node if data == current_node.data
269
+
270
+ mock_node = current_node.class.new data
271
+ if mock_node >= current_node
272
+ _search data, current_node.right
273
+ else
274
+ _search data, current_node.left
275
+ end
276
+ end
277
+
278
+ def is_root? node
279
+ node && @root && node.object_id == @root.object_id
280
+ end
281
+
253
282
  def increment_size!
254
283
  @size += 1
255
284
  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
@@ -1,10 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "utils"
4
+ require_relative "node/leaf_node_comparable"
4
5
  require_relative "node/left_right_element_referencers"
5
6
 
6
7
  class RedBlackTree
7
8
  class Node
9
+ class << self
10
+ def inherited subclass
11
+ subclass.prepend LeafNodeComparable
12
+ end
13
+ end
14
+
8
15
  include Comparable
9
16
 
10
17
  # @return [any] the data/value representing the node
@@ -42,8 +49,8 @@ class RedBlackTree
42
49
  RIGHT = "right"
43
50
  DIRECTIONS = [LEFT, RIGHT].freeze
44
51
 
45
- attr_reader :colour
46
52
  attr_writer :data
53
+ attr_accessor :colour
47
54
  attr_accessor :tree, :parent
48
55
  attr_accessor :left, :right
49
56
  include LeftRightElementReferencers
@@ -65,9 +72,9 @@ class RedBlackTree
65
72
  def position
66
73
  return unless @parent
67
74
 
68
- case self
69
- when @parent.left then LEFT
70
- when @parent.right then RIGHT
75
+ case self.object_id
76
+ when @parent.left.object_id then LEFT
77
+ when @parent.right.object_id then RIGHT
71
78
  else raise StructuralError, "Disowned by parent"
72
79
  end
73
80
  end
@@ -140,13 +147,13 @@ class RedBlackTree
140
147
  self_position = position
141
148
  other_position = other_node.position
142
149
 
143
- if other_node.parent == self
150
+ if other_node.parent.object_id == self.object_id
144
151
  self[other_position] = other_node[other_position]
145
152
  other_node[other_position] = self
146
153
 
147
154
  other_node.parent = @parent
148
155
  @parent = other_node
149
- elsif other_node == @parent
156
+ elsif other_node.object_id == @parent.object_id
150
157
  other_node[self_position] = self[self_position]
151
158
  self[self_position] = other_node
152
159
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedBlackTree
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
@@ -13,7 +13,6 @@ Gem::Specification.new do |spec|
13
13
  spec.license = "MIT"
14
14
  spec.required_ruby_version = ">= 3.0.0"
15
15
 
16
- spec.metadata["homepage_uri"] = spec.homepage
17
16
  spec.metadata["documentation_uri"] = "https://joshuay03.github.io/red-black-tree/"
18
17
  spec.metadata["source_code_uri"] = spec.homepage
19
18
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
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.0
4
+ version: 0.1.2
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-09-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -24,6 +24,7 @@ files:
24
24
  - lib/red-black-tree.rb
25
25
  - lib/red_black_tree/node.rb
26
26
  - lib/red_black_tree/node/leaf_node.rb
27
+ - lib/red_black_tree/node/leaf_node_comparable.rb
27
28
  - lib/red_black_tree/node/left_right_element_referencers.rb
28
29
  - lib/red_black_tree/utils.rb
29
30
  - lib/red_black_tree/version.rb
@@ -32,7 +33,6 @@ homepage: https://github.com/joshuay03/red-black-tree
32
33
  licenses:
33
34
  - MIT
34
35
  metadata:
35
- homepage_uri: https://github.com/joshuay03/red-black-tree
36
36
  documentation_uri: https://joshuay03.github.io/red-black-tree/
37
37
  source_code_uri: https://github.com/joshuay03/red-black-tree
38
38
  changelog_uri: https://github.com/joshuay03/red-black-tree/blob/main/CHANGELOG.md