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?
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 reimplement 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()
}
}
Working Example
use std::cmp::Ordering;
use std::collections::BinaryHeap;
#[derive(PartialEq)]
struct MinFloat(f64);
impl Eq for MinFloat {}
impl PartialOrd for MinFloat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
other.0.partial_cmp(&self.0)
}
}
impl Ord for MinFloat {
fn cmp(&self, other: &MinFloat) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
fn main() {
let mut minheap = BinaryHeap::new();
minheap.push(MinFloat(2.0));
minheap.push(MinFloat(1.0));
minheap.push(MinFloat(42.0));
if let Some(MinFloat(root)) = minheap.pop() {
println!("{:?}", root);
}
}