Rust and Data Structures

Binary Heaps

A binary heap, implemented in the standard library as std::collections::BinaryHeap<T>, is a priority queue backed by a flat contiguous Vec<T> that keeps the highest-priority element at the root (index 0) in $O(1)$ peek time and supports insertion and extraction in $O(\log n)$ time.

Max-Heap Tree Logical View:               Contiguous `Vec<T>` Physical Storage:
            [ 90 ] (idx 0)                Index:  0    1    2    3    4    5
           /      \                              +----+----+----+----+----+----+
     [ 70 ] (1)  [ 80 ] (2)                      | 90 | 70 | 80 | 30 | 50 | 40 |
      /    \      /                              +----+----+----+----+----+----+
   [30](3) [50](4) [40](5)                Parent(i) = (i - 1) / 2
                                          Left(i)   = 2*i + 1,  Right(i) = 2*i + 2

Complexity of BinaryHeap<T> Operations

Operation Method Time Complexity Notes
Inspect Maximum heap.peek() $O(1)$ Returns Option<&T> for the largest element (vec[0]).
Mutate Root in Place heap.peek_mut() $O(\log n)$ on drop Returns a PeekMut guard that sifts the modified root down when dropped (or removes it via PeekMut::pop).
Insert Element heap.push(item) $O(\log n)$ ($O(1)$ average) Appends to the end of the Vec and sifts up.
Extract Maximum heap.pop() $O(\log n)$ Swaps root with last element, pops it, and sifts new root down.
Build Heap from Vec<T> BinaryHeap::from(vec) $O(n)$ (Floyd's algorithm!) Much faster than $n$ calls to push ($O(n \log n)$)!

Turning BinaryHeap (Max-Heap) into a Min-Heap with std::cmp::Reverse

By default, BinaryHeap<T> is a max-heap (pop() returns the largest T according to Ord).

To create a min-heap (needed for Dijkstra's shortest-path algorithm, A* search, timer wheels, or finding the Top $K$ largest items in a stream) without implementing a custom Ord wrapper, wrap each element in std::cmp::Reverse<T>, which inverts the comparison order (a.cmp(b).reverse()):

use std::cmp::Reverse;
use std::collections::BinaryHeap;

/// Finds the `k` largest numbers in an arbitrarily large stream in O(n log k) time
/// and O(k) memory by maintaining a Min-Heap of size `k` using `Reverse`!
pub fn top_k_largest(stream: &[u64], k: usize) -> Vec<u64> {
    if k == 0 {
        return Vec::new();
    }

    // Min-heap of at most `k` elements: `heap.peek()` is the SMALLEST of the top `k` so far!
    let mut min_heap: BinaryHeap<Reverse<u64>> = BinaryHeap::with_capacity(k);

    for &val in stream {
        if min_heap.len() < k {
            min_heap.push(Reverse(val));
        } else if let Some(mut smallest_top) = min_heap.peek_mut() {
            if val > smallest_top.0 {
                // Replace the smallest of the top-k in-place (1 sift-down instead of pop + push!)
                *smallest_top = Reverse(val);
            }
        }
    }

    // Extract in ascending order from the min-heap
    let mut out: Vec<u64> = min_heap.into_iter().map(|Reverse(x)| x).collect();
    out.sort_unstable();
    out
}

fn main() {
    // 1. O(n) bulk heap construction from Vec:
    let mut max_heap = BinaryHeap::from(vec![20, 80, 50, 10, 90]);
    assert_eq!(max_heap.pop(), Some(90));
    assert_eq!(max_heap.pop(), Some(80));

    // 2. Top-3 largest elements using a bounded Min-Heap (`Reverse`):
    let data = [15, 92, 4, 78, 33, 105, 61, 88];
    let top3 = top_k_largest(&data, 3);
    assert_eq!(top3, vec![88, 92, 105]);

    println!("Top 3 largest values: {top3:?}");
}
Top 3 largest values: [88, 92, 105]