我可以修改 BinaryHeap 中不是最高值的值吗?

Can I modify a value inside a BinaryHeap that isn't the top value?

有什么方法可以修改非顶部的最小堆内的值,并更新堆来执行此操作?查看 API,似乎没有任何明确的方法来访问这些元素并调用任何类型的更新方法。

modify a value [...] and have the heap updated

不,标准库的 BinaryHeap 实现不允许调整任何需要更改堆排序的值。文档甚至特别 calls out interior mutability as a bad idea (强调我的):

It is a logic error for an item to be modified in such a way that the item's ordering relative to any other item, as determined by the Ord trait, changes while it is in the heap. This is normally only possible through Cell, RefCell, global state, I/O, or unsafe code.

但是 crates.io 上可能有二进制堆的替代实现。

Is there any way I can modify a value inside a Min-Heap that isn't the top, and have the heap updated to do so?

您可以修改 Min-Heap 中的值,但不能,修改不得改变项目相对于 BinaryHeap 中任何其他项目的顺序。否则,BinaryHeap 将不再是最大堆并且无法更新它。您可以将 BinaryHeap 转换为排序的 Vec、修改、重新排序并将排序的 Vec 转换回 BinaryHeap.

如何在不改变相对顺序的情况下修改 BinaryHeap 中的元素的示例(playground 中的完整示例):

struct Foo(i32, Cell<bool>);
impl Ord for Foo {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

fn main() {
    let mut bh = BinaryHeap::new();
    bh.push(Foo(0, Cell::new(true)));
    bh.push(Foo(1, Cell::new(false)));
    bh.push(Foo(2, Cell::new(false)));

    println!("{:?} => {:?}", bh, bh.peek());
    // prints: 
    // [..., Foo(1, Cell(false))] => Some(Foo(2, Cell(false)))

    // Modify `Foo(1, false)` to `Foo(1, true)`:
    for i in bh.iter() {
        if i.0 == 1 {
            i.1.set(true);
        }
    }

    println!("{:?} => {:?}", bh, bh.peek());
    // prints:
    // [..., Foo(1, Cell(true))] => Some(Foo(2, Cell(false)))
}

这里,FooOrd 实现仅取决于第一个字段 (i32) 的值。也就是说,修改 Foo 的第二个字段 (bool) 的值不会更改任何 Foo 相对于任何其他 Foo 的顺序。

BinaryHeap 的要求让我们更进一步,也修改 Foo 的第一个字段,只要我们不改变任何 Foo 的相对顺序BinaryHeap 中的任何其他 Foo 。也就是说,我们可以在这个特定的 BinaryHeap 中毫无问题地将 Foo(0,..) 更改为 Foo(-3,..)

如果 BinaryHeap 包含 Foo(-2,...),则将 Foo(0,..) 更改为 Foo(-3,..) 会使 BinaryHeap 处于不一致状态:它不会不再是最大堆。不过请注意,在任何地方都不需要 unsafe 代码来修改 BinaryHeap 的元素。也就是说,BinaryHeap 支持所有安全的 Rust 保证(没有未定义的行为),即使存在 "logic errors" 会产生不一致的状态。

您可以做的是将堆转换为向量,对其进行修改,然后将其转换回将重建整个堆的堆 (playground):

let mut bh = BinaryHeap::new();
bh.push(0);
bh.push(1);
bh.push(2);

println!("{:?} => {:?}", bh, bh.peek());

// convert into a vector:
let mut v = bh.into_vec();
println!("{:?}", v);

// modify in a way that alters the order:
v[1] = -1;
println!("{:?}", v);

// convert back into a binary heap
let bh: BinaryHeap<_> = v.into();
println!("{:?} => {:?}", bh, bh.peek());