class PriorityHeap
A priority queue implementation using a standard binary minheap. It uses straight comparison of its contents to determine priority. See https://en.wikipedia.org/wiki/Binary_heap for explanations of the main methods.
Definitions
def initialize
Initializes the heap.
Implementation
def initialize
# The heap is represented with an array containing a binary tree. See
# https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
# is built up.
@contents = []
end
def peek
Signature
-
returns
Object | Nil
the smallest element in the heap without removing it, or nil if the heap is empty.
Implementation
def peek
@contents[0]
end
def size
Signature
-
returns
Integer
the number of elements in the heap.
Implementation
def size
@contents.size
end
def empty?
Signature
-
returns
Boolean
true if the heap is empty, false otherwise.
Implementation
def empty?
@contents.empty?
end
def pop
Removes and returns the smallest element in the heap, or nil if the heap is empty.
Signature
-
returns
Object | Nil
The smallest element in the heap, or nil if the heap is empty.
Implementation
def pop
# If the heap is empty:
if @contents.empty?
return nil
end
# If we have only one item, no swapping is required:
if @contents.size == 1
return @contents.pop
end
# Take the root of the tree:
value = @contents[0]
# Remove the last item in the tree:
last = @contents.pop
# Overwrite the root of the tree with the item:
@contents[0] = last
# Bubble it down into place:
bubble_down(0)
# validate!
return value
end
def push(element)
Add a new element to the heap, then rearrange elements until the heap invariant is true again.
Signature
-
parameter
element
Object
The element to add to the heap.
Implementation
def push(element)
# Insert the item at the end of the heap:
@contents.push(element)
# Bubble it up into position:
bubble_up(@contents.size - 1)
# validate!
return self
end
def concat(elements)
Add multiple elements to the heap efficiently in O(n) time. This is more efficient than calling push multiple times (O(n log n)).
Signature
-
parameter
elements
Array
The elements to add to the heap.
-
returns
self
Returns self for method chaining.
Implementation
def concat(elements)
return self if elements.empty?
# Add all elements to the array without maintaining heap property - O(n)
@contents.concat(elements)
# Rebuild the heap property for the entire array - O(n)
heapify!
return self
end
def clear!
Empties out the heap, discarding all elements
Implementation
def clear!
@contents = []
end
def delete(element)
Remove a specific element from the heap.
O(n) where n is the number of elements in the heap.
Signature
-
parameter
element
Object
The element to remove.
-
returns
Object | Nil
The removed element, or nil if not found.
Implementation
def delete(element)
# Find the index of the element - O(n) linear search
index = @contents.index(element)
return nil unless index
# If it's the last element, just remove it
if index == @contents.size - 1
return @contents.pop
end
# Store the value we're removing
removed_value = @contents[index]
# Replace with the last element
last = @contents.pop
@contents[index] = last
# Restore heap property - might need to bubble up or down
if index > 0 && @contents[index] < @contents[(index - 1) / 2]
# New element is smaller than parent, bubble up
bubble_up(index)
else
# New element might be larger than children, bubble down
bubble_down(index)
end
# validate!
return removed_value
end
def delete_if
Remove elements matching the given block condition by rebuilding the heap.
This is more efficient than multiple delete operations when removing many elements.
O(n) where n is the number of elements in the heap.
Signature
-
returns
Integer
The number of elements removed
Implementation
def delete_if
return enum_for(:delete_if) unless block_given?
original_size = @contents.size
# Filter out elements that match the condition - O(n)
@contents.reject! {|element| yield(element)}
# If we removed elements, rebuild the heap - O(n)
if @contents.size < original_size
heapify!
end
# Return number of elements removed
original_size - @contents.size
end
def valid?
Validate the heap invariant. Every element except the root must not be smaller than its parent element. Note that it MAY be equal.
Implementation
def valid?
# Notice we skip index 0 on purpose, because it has no parent:
(1..(@contents.size - 1)).all? {|index| @contents[index] >= @contents[(index - 1) / 2]}
end
def heapify!
Rebuild the heap property from an arbitrary array in O(n) time. Uses bottom-up heapify algorithm starting from the last non-leaf node.
Implementation
def heapify!
return if @contents.size <= 1
# Start from the last non-leaf node and work backwards to root:
last_non_leaf_index = (@contents.size / 2) - 1
last_non_leaf_index.downto(0) do |index|
bubble_down(index)
end
# validate!
end