red-black-tree 0.1.1 → 0.1.2
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +14 -6
- data/lib/red-black-tree.rb +66 -37
- data/lib/red_black_tree/node/leaf_node_comparable.rb +13 -0
- data/lib/red_black_tree/node.rb +13 -6
- data/lib/red_black_tree/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e609f03254a0ff34b07adf5445e91803cf4f50ffc5ba91b6288c31003218b87a
|
|
4
|
+
data.tar.gz: e1289ab4cefa28f5ae2eca05be45d5a7dbd2fa13a8a5256c81ac21511bb8e0fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 514eee78de1f75f5caaa55d285109d5e64a431a95732b3e6cabbee23e523ddd93b8b1e5e996c4ebcebce86e0ac54a15e0b88299a295515ce592254706f905e1a
|
|
7
|
+
data.tar.gz: '092aaa352db1d9d8d1082393bfff927c35dbb349ffc980720835379c27297f7d47f817edb2eb29a22103bc4beda53c683150b05756444d75b823e673161358e1'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
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
|
+
|
|
3
12
|
## [0.1.1] - 2024-08-04
|
|
4
13
|
|
|
5
14
|
- 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
|
-
|
|
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
|
data/lib/red-black-tree.rb
CHANGED
|
@@ -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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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,
|
|
174
|
+
rotate_sub_tree! node.parent, node.position
|
|
175
|
+
end
|
|
174
176
|
|
|
175
|
-
|
|
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,
|
|
190
|
+
rotate_sub_tree! node.parent, node.position
|
|
186
191
|
|
|
187
192
|
break
|
|
188
193
|
end
|
|
189
194
|
|
|
190
|
-
if node.
|
|
191
|
-
node.sibling.red!
|
|
192
|
-
node.
|
|
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
|
-
|
|
199
|
+
break
|
|
196
200
|
end
|
|
197
201
|
|
|
198
|
-
if node.
|
|
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
|
-
|
|
209
|
-
node.swap_position_with! leaf
|
|
209
|
+
original_node.swap_position_with! LeafNode.new
|
|
210
210
|
end
|
|
211
211
|
end
|
|
212
212
|
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/red_black_tree/node.rb
CHANGED
|
@@ -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
|
|
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.
|
|
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-
|
|
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
|