Skip to content

Commit 1825269

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

File tree

2 files changed

+115
-89
lines changed

2 files changed

+115
-89
lines changed

lib/red-black-tree.rb

Lines changed: 106 additions & 87 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.
@@ -363,8 +283,8 @@ def _search_by_data data, node
363283
return if node.nil? || node.leaf?
364284
return node if data == node.data
365285

366-
mock_node = node.class.new data
367-
if mock_node >= node
286+
comparison = data <=> node.data
287+
if comparison && comparison >= 0
368288
_search_by_data data, node.right
369289
else
370290
_search_by_data data, node.left
@@ -401,6 +321,105 @@ def decrement_size!
401321
@size -= 1
402322
end
403323

324+
def delete_node_with_two_children! node
325+
is_root = is_root? node
326+
327+
successor = node.left
328+
successor = successor.right until successor.right.leaf?
329+
node.swap_colour_with! successor
330+
node.swap_position_with! successor
331+
node.swap_position_with! LeafNode.new
332+
333+
@root = successor if is_root
334+
end
335+
336+
def delete_node_with_one_child! node
337+
is_root = is_root? node
338+
339+
valid_child = node.children.find(&:valid?)
340+
valid_child.black!
341+
node.swap_position_with! valid_child
342+
node.swap_position_with! LeafNode.new
343+
344+
@root = valid_child if is_root
345+
end
346+
347+
def delete_leaf_node! node, original_node
348+
if is_root? node
349+
@root = nil
350+
elsif node.red?
351+
node.swap_position_with! LeafNode.new
352+
else
353+
rebalance_after_deletion! node
354+
original_node.swap_position_with! LeafNode.new
355+
end
356+
end
357+
358+
def rebalance_after_deletion! node
359+
loop do
360+
if node.sibling && node.sibling.valid? && node.sibling.red?
361+
node.parent.red!
362+
node.sibling.black!
363+
rotate_sub_tree! node.parent, node.position
364+
end
365+
366+
if node.close_nephew && node.close_nephew.valid? && node.close_nephew.red?
367+
node.sibling.red! unless node.sibling.leaf?
368+
node.close_nephew.black!
369+
rotate_sub_tree! node.sibling, node.opposite_position
370+
end
371+
372+
if node.distant_nephew && node.distant_nephew.valid? && node.distant_nephew.red?
373+
case node.parent.colour
374+
when Node::RED then node.sibling.red!
375+
when Node::BLACK then node.sibling.black!
376+
end
377+
node.parent.black!
378+
node.distant_nephew.black!
379+
rotate_sub_tree! node.parent, node.position
380+
381+
break
382+
end
383+
384+
if node.parent && node.parent.red?
385+
node.sibling.red! unless node.sibling.leaf?
386+
node.parent.black!
387+
388+
break
389+
end
390+
391+
if node.sibling && !node.sibling.leaf?
392+
node.sibling.red!
393+
end
394+
395+
break unless node = node.parent
396+
end
397+
end
398+
399+
def rebalance_after_insertion! node
400+
while node.parent && node.parent.red? do
401+
if node.parent.sibling && node.parent.sibling.red?
402+
node.parent.black!
403+
node.parent.sibling.black!
404+
node.parent.parent.red!
405+
node = node.parent.parent
406+
else
407+
opp_direction = node.opposite_position
408+
if node.parent.position == opp_direction
409+
rotate_sub_tree! node.parent, opp_direction
410+
node = node[opp_direction]
411+
end
412+
413+
opp_direction = node.opposite_position
414+
rotate_sub_tree! node.parent.parent, opp_direction
415+
node.parent.black!
416+
node.parent[opp_direction].red!
417+
end
418+
419+
@root.black!
420+
end
421+
end
422+
404423
def update_left_most_node!
405424
unless @root
406425
@left_most_node = nil

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)