Advanced Projects

Binary Heap Implementation

A binary heap is a complete binary tree stored compactly inside a flat Vec<T> without node pointers, satisfying the heap property where every parent compares greater than or equal to (max-heap) or less than or equal to (min-heap) its children. Supporting indexed key updates (decrease_key / update_priority) via an auxiliary handle map makes the heap directly usable in graph shortest-path and task-scheduling algorithms.

Array-Backed Complete Binary Tree Layout

Index mapping for 0-based array:
  parent(i)      = (i - 1) / 2
  left_child(i)  = 2 * i + 1
  right_child(i) = 2 * i + 2

        [0: prio=2]
        /         \
  [1: prio=5]   [2: prio=4]
    /     \       /
[3: 9]  [4: 7] [5: 8]

Flat Vec storage: [ (2), (5), (4), (9), (7), (8) ]
Operation Restoration Direction Time Complexity
push(key, priority) Append at index len, then sift_up(len) $O(\log n)$
pop() Swap root 0 with len - 1, pop back, then sift_down(0) $O(\log n)$
peek() Read index 0 $O(1)$
update_priority(key, new_p) Look up index in pos_map, update priority, then sift_up and sift_down $O(\log n)$
from_vec(items) (Floyd's build-heap) Run sift_down from (len / 2 - 1) down to 0 $O(n)$

Complete Indexed Min-Heap Implementation

use std::collections::HashMap;
use std::hash::Hash;

#[derive(Debug, Clone)]
struct HeapNode<K, P> {
    key: K,
    priority: P,
}

pub struct IndexedMinHeap<K, P> {
    nodes: Vec<HeapNode<K, P>>,
    positions: HashMap<K, usize>,
}

impl<K: Hash + Eq + Clone, P: Ord + Clone> IndexedMinHeap<K, P> {
    pub fn new() -> Self {
        Self {
            nodes: Vec::new(),
            positions: HashMap::new(),
        }
    }

    pub fn from_entries(entries: Vec<(K, P)>) -> Self {
        let mut nodes = Vec::with_capacity(entries.len());
        let mut positions = HashMap::with_capacity(entries.len());
        for (idx, (key, priority)) in entries.into_iter().enumerate() {
            positions.insert(key.clone(), idx);
            nodes.push(HeapNode { key, priority });
        }
        let mut heap = Self { nodes, positions };
        // Floyd's linear-time O(n) heap construction
        if heap.nodes.len() > 1 {
            for i in (0..=(heap.nodes.len() / 2 - 1)).rev() {
                heap.sift_down(i);
            }
        }
        heap
    }

    pub fn len(&self) -> usize {
        self.nodes.len()
    }

    pub fn is_empty(&self) -> bool {
        self.nodes.is_empty()
    }

    pub fn peek(&self) -> Option<(&K, &P)> {
        self.nodes.first().map(|n| (&n.key, &n.priority))
    }

    pub fn priority_of(&self, key: &K) -> Option<&P> {
        let &idx = self.positions.get(key)?;
        Some(&self.nodes[idx].priority)
    }

    pub fn push_or_update(&mut self, key: K, priority: P) -> bool {
        if let Some(&idx) = self.positions.get(&key) {
            self.nodes[idx].priority = priority;
            self.sift_up(idx);
            self.sift_down(idx);
            false
        } else {
            let idx = self.nodes.len();
            self.positions.insert(key.clone(), idx);
            self.nodes.push(HeapNode { key, priority });
            self.sift_up(idx);
            true
        }
    }

    pub fn pop(&mut self) -> Option<(K, P)> {
        if self.nodes.is_empty() {
            return None;
        }
        let last_idx = self.nodes.len() - 1;
        self.swap_nodes(0, last_idx);
        let removed = self.nodes.pop().unwrap();
        self.positions.remove(&removed.key);

        if !self.nodes.is_empty() {
            self.sift_down(0);
        }
        Some((removed.key, removed.priority))
    }

    fn swap_nodes(&mut self, a: usize, b: usize) {
        if a == b {
            return;
        }
        self.nodes.swap(a, b);
        let key_a = self.nodes[a].key.clone();
        let key_b = self.nodes[b].key.clone();
        self.positions.insert(key_a, a);
        self.positions.insert(key_b, b);
    }

    fn sift_up(&mut self, mut idx: usize) {
        while idx > 0 {
            let parent = (idx - 1) / 2;
            if self.nodes[idx].priority < self.nodes[parent].priority {
                self.swap_nodes(idx, parent);
                idx = parent;
            } else {
                break;
            }
        }
    }

    fn sift_down(&mut self, mut idx: usize) {
        let len = self.nodes.len();
        loop {
            let left = 2 * idx + 1;
            let right = 2 * idx + 2;
            let mut smallest = idx;

            if left < len && self.nodes[left].priority < self.nodes[smallest].priority {
                smallest = left;
            }
            if right < len && self.nodes[right].priority < self.nodes[smallest].priority {
                smallest = right;
            }
            if smallest != idx {
                self.swap_nodes(idx, smallest);
                idx = smallest;
            } else {
                break;
            }
        }
    }
}

fn main() {
    let initial = vec![
        ("worker-a", 45),
        ("worker-b", 12),
        ("worker-c", 80),
        ("worker-d", 25),
        ("worker-e", 5),
    ];

    let mut heap = IndexedMinHeap::from_entries(initial);
    println!("After O(n) Floyd build-heap, peek = {:?}", heap.peek());

    // Dynamically lower the priority cost of 'worker-c' from 80 to 2
    heap.push_or_update("worker-c", 2);
    println!(
        "After updating 'worker-c' priority to 2, peek = {:?}",
        heap.peek()
    );

    println!("\nDrain order:");
    while let Some((worker, cost)) = heap.pop() {
        println!("  {} -> cost {}", worker, cost);
    }
}
After O(n) Floyd build-heap, peek = Some((&"worker-e", &5))
After updating 'worker-c' priority to 2, peek = Some((&"worker-c", &2))

Drain order:
  worker-c -> cost 2
  worker-e -> cost 5
  worker-b -> cost 12
  worker-d -> cost 25
  worker-a -> cost 45