Skip to content

Commit a254ca4

Browse files
committed
Refactor shared binary heap internals into a utility module
Extract identical helper methods from BinaryHeapNode and BinaryMaxHeapNode into Tree::Utils::HeapSharedMethods to reduce duplication while preserving public behavior.
1 parent 8b52f3f commit a254ca4

4 files changed

Lines changed: 149 additions & 186 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Changes section to scan for breaking or behavioral changes.
1414
`Tree::BinaryHeapNode#pop`, `Tree::BinaryMaxHeapNode#pop`,
1515
and `Tree::TrieNode#search`.
1616
* Add alias-focused regression tests to lock these compatibility contracts.
17+
* Refactor shared binary heap internals into
18+
`Tree::Utils::HeapSharedMethods` to reduce duplication between min-heap and
19+
max-heap implementations without changing public behavior.
1720
* Make `Tree::TrieNode#<<` use trie word-insert semantics (`insert(word)`)
1821
instead of generic child-node attachment semantics.
1922
* Add `<<` insertion shorthand to `Tree::AATree` and `Tree::BTree` for

lib/tree/binaryheap.rb

Lines changed: 3 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
# frozen_string_literal: true
3939

4040
require_relative 'binarytree'
41+
require_relative 'utils/heap_shared_methods'
4142

4243
module Tree
4344
# Provides a Binary Heap (min-heap) implementation. This node allows only two
@@ -56,6 +57,8 @@ module Tree
5657
# This inherits from the {Tree::BinaryTreeNode} class.
5758
#
5859
class BinaryHeapNode < BinaryTreeNode
60+
include Tree::Utils::HeapSharedMethods
61+
5962
# Inserts the specified node into the heap, based on the node content.
6063
#
6164
# @param [Tree::BinaryHeapNode, String, Symbol] node_or_name The node
@@ -222,24 +225,6 @@ def validate_key!(key)
222225
raise ArgumentError, 'Binary heap key (content) must not be nil.' if key.nil?
223226
end
224227

225-
# +true+ if the root represents an empty heap.
226-
#
227-
# @param [Tree::BinaryHeapNode] node The node to check.
228-
# @return [Boolean] +true+ if empty.
229-
def empty_root?(node)
230-
node.root? && node.content.nil? && !node.children?
231-
end
232-
233-
# Returns the heap size treating an empty root as size 0.
234-
#
235-
# @param [Tree::BinaryHeapNode] node The root node.
236-
# @return [Integer] The heap size.
237-
def heap_size(node)
238-
return 0 if empty_root?(node)
239-
240-
node.size
241-
end
242-
243228
# Insert a node at the next available index to keep the heap complete.
244229
#
245230
# @param [Tree::BinaryHeapNode] tree_root The root node.
@@ -258,31 +243,6 @@ def insert_at_index(tree_root, node)
258243
end
259244
end
260245

261-
# Return the node at the given heap index.
262-
#
263-
# @param [Tree::BinaryHeapNode] tree_root The root node.
264-
# @param [Integer] index The heap index (1-based).
265-
# @return [Tree::BinaryHeapNode, nil] The node at the index.
266-
def node_for_index(tree_root, index)
267-
return tree_root if index == 1
268-
269-
current = tree_root
270-
path_bits = index.to_s(2).chars.drop(1)
271-
path_bits.each do |bit|
272-
current = bit == '0' ? current.left_child : current.right_child
273-
return nil unless current
274-
end
275-
current
276-
end
277-
278-
# Return the last node in the heap (right-most node at the deepest level).
279-
#
280-
# @param [Tree::BinaryHeapNode] tree_root The root node.
281-
# @return [Tree::BinaryHeapNode] The last node.
282-
def last_node(tree_root)
283-
node_for_index(tree_root, heap_size(tree_root))
284-
end
285-
286246
# Restore heap ordering by bubbling the node upward.
287247
#
288248
# @param [Tree::BinaryHeapNode] node The node to lift.
@@ -317,56 +277,6 @@ def heapify_down(node)
317277
end
318278
end
319279

320-
# Swap the content values of two nodes.
321-
#
322-
# @param [Tree::BinaryHeapNode] left The first node.
323-
# @param [Tree::BinaryHeapNode] right The second node.
324-
# @return [void]
325-
def swap_contents(left, right)
326-
left.content, right.content = right.content, left.content
327-
end
328-
329-
# Remove the last node from the heap.
330-
#
331-
# @param [Tree::BinaryHeapNode] node The last node.
332-
# @return [void]
333-
def remove_last_node(node)
334-
if node.root?
335-
node.instance_variable_set(:@content, nil)
336-
return
337-
end
338-
339-
if node.left_child?
340-
node.parent.left_child = nil
341-
else
342-
node.parent.right_child = nil
343-
end
344-
end
345-
346-
# Remove an arbitrary node from the heap.
347-
#
348-
# @param [Tree::BinaryHeapNode] node The node to remove.
349-
# @return [void]
350-
def remove_node(node)
351-
tree_root = root
352-
return if empty_root?(tree_root)
353-
354-
if heap_size(tree_root) == 1
355-
tree_root.instance_variable_set(:@content, nil)
356-
return
357-
end
358-
359-
last = last_node(tree_root)
360-
if node.equal?(last)
361-
remove_last_node(last)
362-
return
363-
end
364-
365-
swap_contents(node, last)
366-
remove_last_node(last)
367-
rebalance_after_swap(node)
368-
end
369-
370280
# Rebalance heap ordering after swapping in a replacement node.
371281
#
372282
# @param [Tree::BinaryHeapNode] node The node to rebalance from.

lib/tree/binarymaxheap.rb

Lines changed: 3 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
# frozen_string_literal: true
3939

4040
require_relative 'binarytree'
41+
require_relative 'utils/heap_shared_methods'
4142

4243
module Tree
4344
# Provides a Binary Max-Heap implementation. This node allows only two child
@@ -56,6 +57,8 @@ module Tree
5657
# This inherits from the {Tree::BinaryTreeNode} class.
5758
#
5859
class BinaryMaxHeapNode < BinaryTreeNode
60+
include Tree::Utils::HeapSharedMethods
61+
5962
# Inserts the specified node into the heap, based on the node content.
6063
#
6164
# @param [Tree::BinaryMaxHeapNode, String, Symbol] node_or_name The node
@@ -224,24 +227,6 @@ def validate_key!(key)
224227
raise ArgumentError, 'Binary max-heap key (content) must not be nil.' if key.nil?
225228
end
226229

227-
# +true+ if the root represents an empty heap.
228-
#
229-
# @param [Tree::BinaryMaxHeapNode] node The node to check.
230-
# @return [Boolean] +true+ if empty.
231-
def empty_root?(node)
232-
node.root? && node.content.nil? && !node.children?
233-
end
234-
235-
# Returns the heap size treating an empty root as size 0.
236-
#
237-
# @param [Tree::BinaryMaxHeapNode] node The root node.
238-
# @return [Integer] The heap size.
239-
def heap_size(node)
240-
return 0 if empty_root?(node)
241-
242-
node.size
243-
end
244-
245230
# Insert a node at the next available index to keep the heap complete.
246231
#
247232
# @param [Tree::BinaryMaxHeapNode] tree_root The root node.
@@ -260,31 +245,6 @@ def insert_at_index(tree_root, node)
260245
end
261246
end
262247

263-
# Return the node at the given heap index.
264-
#
265-
# @param [Tree::BinaryMaxHeapNode] tree_root The root node.
266-
# @param [Integer] index The heap index (1-based).
267-
# @return [Tree::BinaryMaxHeapNode, nil] The node at the index.
268-
def node_for_index(tree_root, index)
269-
return tree_root if index == 1
270-
271-
current = tree_root
272-
path_bits = index.to_s(2).chars.drop(1)
273-
path_bits.each do |bit|
274-
current = bit == '0' ? current.left_child : current.right_child
275-
return nil unless current
276-
end
277-
current
278-
end
279-
280-
# Return the last node in the heap (right-most node at the deepest level).
281-
#
282-
# @param [Tree::BinaryMaxHeapNode] tree_root The root node.
283-
# @return [Tree::BinaryMaxHeapNode] The last node.
284-
def last_node(tree_root)
285-
node_for_index(tree_root, heap_size(tree_root))
286-
end
287-
288248
# Restore heap ordering by bubbling the node upward.
289249
#
290250
# @param [Tree::BinaryMaxHeapNode] node The node to lift.
@@ -319,56 +279,6 @@ def heapify_down(node)
319279
end
320280
end
321281

322-
# Swap the content values of two nodes.
323-
#
324-
# @param [Tree::BinaryMaxHeapNode] left The first node.
325-
# @param [Tree::BinaryMaxHeapNode] right The second node.
326-
# @return [void]
327-
def swap_contents(left, right)
328-
left.content, right.content = right.content, left.content
329-
end
330-
331-
# Remove the last node from the heap.
332-
#
333-
# @param [Tree::BinaryMaxHeapNode] node The last node.
334-
# @return [void]
335-
def remove_last_node(node)
336-
if node.root?
337-
node.instance_variable_set(:@content, nil)
338-
return
339-
end
340-
341-
if node.left_child?
342-
node.parent.left_child = nil
343-
else
344-
node.parent.right_child = nil
345-
end
346-
end
347-
348-
# Remove an arbitrary node from the heap.
349-
#
350-
# @param [Tree::BinaryMaxHeapNode] node The node to remove.
351-
# @return [void]
352-
def remove_node(node)
353-
tree_root = root
354-
return if empty_root?(tree_root)
355-
356-
if heap_size(tree_root) == 1
357-
tree_root.instance_variable_set(:@content, nil)
358-
return
359-
end
360-
361-
last = last_node(tree_root)
362-
if node.equal?(last)
363-
remove_last_node(last)
364-
return
365-
end
366-
367-
swap_contents(node, last)
368-
remove_last_node(last)
369-
rebalance_after_swap(node)
370-
end
371-
372282
# Rebalance heap ordering after swapping in a replacement node.
373283
#
374284
# @param [Tree::BinaryMaxHeapNode] node The node to rebalance from.

0 commit comments

Comments
 (0)