Skip to content

Commit 22fa712

Browse files
committed
refactor to slim down and de-duplicate some methods
1 parent cd1eaeb commit 22fa712

File tree

3 files changed

+140
-112
lines changed

3 files changed

+140
-112
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## [Unreleased]
22

3+
- Refactor to slim down and de-duplicate some methods
4+
35
## [0.1.7] - 2025-01-05
46

57
- Change `RedBlackTree#delete!` return value to the deleted node

lib/red-black-tree.rb

Lines changed: 129 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -106,27 +106,7 @@ def insert! node, target_parent = nil, direction = nil
106106
node.parent = target_parent
107107
node.red!
108108

109-
while node.parent && node.parent.red? do
110-
if node.parent.sibling && node.parent.sibling.red?
111-
node.parent.black!
112-
node.parent.sibling.black!
113-
node.parent.parent.red!
114-
node = node.parent.parent
115-
else
116-
opp_direction = node.opposite_position
117-
if node.parent.position == opp_direction
118-
rotate_sub_tree! node.parent, opp_direction
119-
node = node[opp_direction]
120-
end
121-
122-
opp_direction = node.opposite_position
123-
rotate_sub_tree! node.parent.parent, opp_direction
124-
node.parent.black!
125-
node.parent[opp_direction].red!
126-
end
127-
128-
@root.black!
129-
end
109+
rebalance_after_insertion! node
130110
end
131111

132112
node.validate! @root == node
@@ -153,71 +133,11 @@ def delete! node
153133
original_node = node
154134

155135
if node.children_are_valid?
156-
is_root = is_root? node
157-
158-
successor = node.left
159-
successor = successor.left until successor.left.leaf?
160-
node.swap_colour_with! successor
161-
node.swap_position_with! successor
162-
node.swap_position_with! LeafNode.new
163-
164-
@root = successor if is_root
136+
delete_node_with_two_children! node
165137
elsif node.single_child_is_valid?
166-
is_root = is_root? node
167-
168-
valid_child = node.children.find(&:valid?)
169-
valid_child.black!
170-
node.swap_position_with! valid_child
171-
node.swap_position_with! LeafNode.new
172-
173-
@root = valid_child if is_root
138+
delete_node_with_one_child! node
174139
elsif node.children_are_leaves?
175-
if is_root? node
176-
@root = nil
177-
elsif node.red?
178-
node.swap_position_with! LeafNode.new
179-
else
180-
loop do
181-
if node.sibling && node.sibling.valid? && node.sibling.red?
182-
node.parent.red!
183-
node.sibling.black!
184-
rotate_sub_tree! node.parent, node.position
185-
end
186-
187-
if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
188-
node.sibling.red! unless node.sibling.leaf?
189-
node.close_nephew.black!
190-
rotate_sub_tree! node.sibling, node.opposite_position
191-
end
192-
193-
if node.distant_nephew && node.distant_nephew.valid? && node.distant_nephew.red?
194-
case node.parent.colour
195-
when Node::RED then node.sibling.red!
196-
when Node::BLACK then node.sibling.black!
197-
end
198-
node.parent.black!
199-
node.distant_nephew.black!
200-
rotate_sub_tree! node.parent, node.position
201-
202-
break
203-
end
204-
205-
if node.parent && node.parent.red?
206-
node.sibling.red! unless node.sibling.leaf?
207-
node.parent.black!
208-
209-
break
210-
end
211-
212-
if node.sibling && !node.sibling.leaf?
213-
node.sibling.red!
214-
end
215-
216-
break unless node = node.parent
217-
end
218-
219-
original_node.swap_position_with! LeafNode.new
220-
end
140+
delete_leaf_node! node, original_node
221141
end
222142

223143
original_node.tree = nil
@@ -270,7 +190,7 @@ def select &block
270190
#
271191
# @return [true, false]
272192
def include? data
273-
!!search(data)
193+
!search(data).nil?
274194
end
275195

276196
# Traverses the tree in pre-order and yields each node.
@@ -333,6 +253,29 @@ def traverse_level_order &block
333253

334254
private
335255

256+
def is_root? node
257+
node && @root && node.object_id == @root.object_id
258+
end
259+
260+
def increment_size!
261+
@size += 1
262+
end
263+
264+
def decrement_size!
265+
@size -= 1
266+
end
267+
268+
def update_left_most_node!
269+
unless @root
270+
@left_most_node = nil
271+
return
272+
end
273+
274+
current = @root
275+
current = current.left until current.left.leaf?
276+
@left_most_node = current
277+
end
278+
336279
# Rotates a (sub-)tree starting from the given node in the given direction.
337280
#
338281
# @param node [RedBlackTree::Node] the root node of the sub-tree
@@ -359,12 +302,111 @@ def rotate_sub_tree! node, direction
359302
opp_direction_child
360303
end
361304

305+
def rebalance_after_insertion! node
306+
while node.parent && node.parent.red? do
307+
if node.parent.sibling && node.parent.sibling.red?
308+
node.parent.black!
309+
node.parent.sibling.black!
310+
node.parent.parent.red!
311+
node = node.parent.parent
312+
else
313+
opp_direction = node.opposite_position
314+
if node.parent.position == opp_direction
315+
rotate_sub_tree! node.parent, opp_direction
316+
node = node[opp_direction]
317+
end
318+
319+
opp_direction = node.opposite_position
320+
rotate_sub_tree! node.parent.parent, opp_direction
321+
node.parent.black!
322+
node.parent[opp_direction].red!
323+
end
324+
325+
@root.black!
326+
end
327+
end
328+
329+
def delete_node_with_two_children! node
330+
is_root = is_root? node
331+
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
337+
338+
@root = successor if is_root
339+
end
340+
341+
def delete_node_with_one_child! node
342+
is_root = is_root? node
343+
344+
valid_child = node.children.find(&:valid?)
345+
valid_child.black!
346+
node.swap_position_with! valid_child
347+
node.swap_position_with! LeafNode.new
348+
349+
@root = valid_child if is_root
350+
end
351+
352+
def delete_leaf_node! node, original_node
353+
if is_root? node
354+
@root = nil
355+
elsif node.red?
356+
node.swap_position_with! LeafNode.new
357+
else
358+
rebalance_after_deletion! node
359+
original_node.swap_position_with! LeafNode.new
360+
end
361+
end
362+
363+
def rebalance_after_deletion! node
364+
loop do
365+
if node.sibling && node.sibling.valid? && node.sibling.red?
366+
node.parent.red!
367+
node.sibling.black!
368+
rotate_sub_tree! node.parent, node.position
369+
end
370+
371+
if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
372+
node.sibling.red! unless node.sibling.leaf?
373+
node.close_nephew.black!
374+
rotate_sub_tree! node.sibling, node.opposite_position
375+
end
376+
377+
if node.distant_nephew && node.distant_nephew.valid? && node.distant_nephew.red?
378+
case node.parent.colour
379+
when Node::RED then node.sibling.red!
380+
when Node::BLACK then node.sibling.black!
381+
end
382+
node.parent.black!
383+
node.distant_nephew.black!
384+
rotate_sub_tree! node.parent, node.position
385+
386+
break
387+
end
388+
389+
if node.parent && node.parent.red?
390+
node.sibling.red! unless node.sibling.leaf?
391+
node.parent.black!
392+
393+
break
394+
end
395+
396+
if node.sibling && !node.sibling.leaf?
397+
node.sibling.red!
398+
end
399+
400+
break unless node = node.parent
401+
end
402+
end
403+
362404
def _search_by_data data, node
363405
return if node.nil? || node.leaf?
364406
return node if data == node.data
365407

366-
mock_node = node.class.new data
367-
if mock_node >= node
408+
comparison = data <=> node.data
409+
if comparison && comparison >= 0
368410
_search_by_data data, node.right
369411
else
370412
_search_by_data data, node.left
@@ -388,27 +430,4 @@ def _select_by_block block, node
388430
end
389431
end
390432
end
391-
392-
def is_root? node
393-
node && @root && node.object_id == @root.object_id
394-
end
395-
396-
def increment_size!
397-
@size += 1
398-
end
399-
400-
def decrement_size!
401-
@size -= 1
402-
end
403-
404-
def update_left_most_node!
405-
unless @root
406-
@left_most_node = nil
407-
return
408-
end
409-
410-
current = @root
411-
current = current.left until current.left.leaf?
412-
@left_most_node = current
413-
end
414433
end

lib/red_black_tree/node/left_right_element_referencers.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ class RedBlackTree
44
class Node
55
module LeftRightElementReferencers # @private
66
def [] direction
7+
validate_direction! direction
78
case direction
89
when Node::LEFT then @left
910
when Node::RIGHT then @right
10-
else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}, got #{direction}"
1111
end
1212
end
1313

1414
def []= direction, node
15+
validate_direction! direction
1516
case direction
1617
when Node::LEFT then @left = node
1718
when Node::RIGHT then @right = node
18-
else raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}, got #{direction}"
1919
end
2020
end
21+
22+
private
23+
24+
def validate_direction! direction
25+
return if [Node::LEFT, Node::RIGHT].include?(direction)
26+
raise ArgumentError, "Direction must be one of #{Implementation::DIRECTIONS}, got #{direction}"
27+
end
2128
end
2229
end
2330
end

0 commit comments

Comments
 (0)