I want to populate a binary heap with floats--more specifically, I'd like to implement a min-heap.
It seems that floats do not support Ord
and thus aren't usable out of the box. My attempts to wrap them have so far failed. However it seems that if I could wrap them then I could also implement Ord
in such a way that it would effectively make BinaryHeap
a min-heap.
Here's an example of a wrapper I tried:
#[derive(PartialEq, PartialOrd)]
struct MinNonNan(f64);
impl Eq for MinNonNan {}
impl Ord for MinNonNan {
fn cmp(&self, other: &MinNonNan) -> Ordering {
let ord = self.partial_cmp(other).unwrap();
match ord {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
Ordering::Equal => ord
}
}
}
The problem is pop
returns the values as though it were a max-heap.
What exactly do I need to do to populate a BinaryHeap
with f64
values as a min-heap?
To build a min heap we:Create a new child node at the end of the heap (last level). Add the new key to that node (append it to the array). Move the child up until you reach the root node and the heap property is satisfied.
A Binary Heap is a complete binary tree which is either Min Heap or Max Heap. In a Max Binary Heap, the key at root must be maximum among all keys present in Binary Heap. This property must be recursively true for all nodes in Binary Tree. Min Binary Heap is similar to Min Heap.
A Min Heap Binary Tree is a Binary Tree where the root node has the minimum key in the tree. The above definition holds true for all sub-trees in the tree. This is called the Min Heap property. Almost every node other than the last two layers must have two children.
A max-heap is a complete binary tree in which the value in each internal node is greater than or equal to the values in the children of that node. Mapping the elements of a heap into an array is trivial: if a node is stored an index k, then its left child is stored at index 2k + 1 and its right child at index 2k + 2.
Instead of writing your own MinNonNan
, consider using the ordered-float crate + the std::cmp::Reverse
type.
type MinNonNan = Reverse<NotNan<f64>>;
Since you are #[derive]
ing PartialOrd
, the .gt()
, .lt()
etc methods still compare normally, i.e. MinNonNan(42.0) < MinNonNan(47.0)
is still true. The Ord
bound only restricts you to provide strictly-ordered types, it doesn't mean the implementation will use .cmp()
instead of <
/>
/<=
/>=
everywhere, nor the compiler will suddenly change those operators to use the Ord
implementation.
If you want to flip the order, you need to implement PartialOrd
as well.
#[derive(PartialEq)]
struct MinNonNan(f64);
impl PartialOrd for MinNonNan {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
other.0.partial_cmp(&self.0)
}
}
impl Ord for MinNonNan {
fn cmp(&self, other: &MinNonNan) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
use ordered_float::NotNan; // 2.7.0
use std::{cmp::Reverse, collections::BinaryHeap};
fn main() {
let mut minheap = BinaryHeap::new();
minheap.push(Reverse(NotNan::new(2.0).unwrap()));
minheap.push(Reverse(NotNan::new(1.0).unwrap()));
minheap.push(Reverse(NotNan::new(42.0).unwrap()));
if let Some(Reverse(nn)) = minheap.pop() {
println!("{}", nn.into_inner());
}
}
use std::{cmp::Ordering, collections::BinaryHeap};
#[derive(PartialEq)]
struct MinNonNan(f64);
impl Eq for MinNonNan {}
impl PartialOrd for MinNonNan {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
other.0.partial_cmp(&self.0)
}
}
impl Ord for MinNonNan {
fn cmp(&self, other: &MinNonNan) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
fn main() {
let mut minheap = BinaryHeap::new();
minheap.push(MinNonNan(2.0));
minheap.push(MinNonNan(1.0));
minheap.push(MinNonNan(42.0));
if let Some(MinNonNan(root)) = minheap.pop() {
println!("{:?}", root);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With