From df7536b82b99a2b8760ac0a8861df2f1b4472576 Mon Sep 17 00:00:00 2001 From: Michael Bradley Date: Sat, 4 Jan 2025 00:15:27 +1300 Subject: [PATCH] Add binary min-heap --- src/backing/pure/binary_heap.rs | 146 ++++++++++++++++++++++++++++++++ src/backing/pure/mod.rs | 1 + 2 files changed, 147 insertions(+) create mode 100644 src/backing/pure/binary_heap.rs diff --git a/src/backing/pure/binary_heap.rs b/src/backing/pure/binary_heap.rs new file mode 100644 index 0000000..5195756 --- /dev/null +++ b/src/backing/pure/binary_heap.rs @@ -0,0 +1,146 @@ +use std::fmt; + +use super::PureBacking; + +/// A binary min-heap backed by an array +#[derive(Debug)] +pub struct BinaryHeap { + data: Vec, +} + +/// Indicates why a sift failed +#[derive(Debug, Clone)] +struct SiftError { + /// The index that couldn't be sifted + index: usize, + /// The length of the array + len: usize, +} + +impl SiftError { + /// Instantiates a `SiftError` + fn new(index: usize, len: usize) -> Self { + Self { index, len } + } +} + +impl fmt::Display for SiftError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Could not sift index {} of {}", self.index, self.len) + } +} + +/// Whether a sift operation succeeded +type SiftResult = Result<(), SiftError>; + +impl BinaryHeap { + /// Fix an index representing a node with valid children but that may violate the heap property compared to its immediate parent + fn sift_up(&mut self, i: usize) -> SiftResult { + if i == 0 { + // Base case, at root so nothing to do + Ok(()) + } else if let Some(child) = self.data.get(i).copied() { + let parent_index = (i - 1) / 2; + // Check if the heap property is violated + if child < self.data[parent_index] { + // Swap child with parent + self.data[i] = self.data[parent_index]; + self.data[parent_index] = child; + + // Repeat process with parent + self.sift_up(parent_index) + } else { + // Sift complete + Ok(()) + } + } else { + // Tried to sift a non-existent index + Err(SiftError::new(i, self.data.len())) + } + } + /// Fix an index representing a node with that doesn't violate the heap property, but may have immediate children that do + fn sift_down(&mut self, i: usize) -> SiftResult { + if i > self.data.len() { + // Tried to sift a non-existent index + Err(SiftError::new(i, self.data.len())) + } else { + if let Some(first_child) = self.data.get(i * 2 + 1).copied() { + let smaller_child_index; + let smaller_child; + + // Find the smallest child and its index + if let Some(second_child) = self.data.get(i * 2 + 2).copied() { + // Both children, use the smaller one + if first_child < second_child { + smaller_child = first_child; + smaller_child_index = i * 2 + 1; + } else { + smaller_child = second_child; + smaller_child_index = i * 2 + 2; + } + } else { + // Only one child, no choice + smaller_child = first_child; + smaller_child_index = i * 2 + 1; + } + + if smaller_child < self.data[i] { + // Swap parent with child + self.data[smaller_child_index] = self.data[i]; + self.data[i] = smaller_child; + + // Repeat process with child + self.sift_down(smaller_child_index) + } else { + // Heap property satisfied, we're done + Ok(()) + } + } else { + // Base case, no children so nothing to do + Ok(()) + } + } + } +} + +impl PureBacking for BinaryHeap { + fn new() -> Self { + Self { data: vec![] } + } + + fn add(&mut self, item: T) { + // Append item + self.data.push(item); + // Sift up to retain heap property + self.sift_up(self.data.len() - 1).unwrap(); + } + + fn pop(&mut self) -> Option { + match self.data.len() { + // No extra processing + 0 | 1 => self.data.pop(), + _ => { + let last = self + .data + .pop() + .expect("Vector claimed not to be empty but was"); + let root = self + .data + .get_mut(0) + .expect("Vector claimed to have multiple items but didn't"); + + // Move final item to the root and sift down to regain heap property + let best = *root; + *root = last; + self.sift_down(0).unwrap(); + + // Return original root + Some(best) + } + } + } + + fn len(&self) -> usize { + self.data.len() + } +} diff --git a/src/backing/pure/mod.rs b/src/backing/pure/mod.rs index b2d076b..24f69c9 100644 --- a/src/backing/pure/mod.rs +++ b/src/backing/pure/mod.rs @@ -1,4 +1,5 @@ /// Data structures for the "pure" min-queues, supporting duplicates but no arbitrary updates +pub mod binary_heap; /// A data structure usable for backing a "pure" queue pub trait PureBacking {