diff --git a/libs/interval_heap/Cargo.toml b/libs/interval_heap/Cargo.toml new file mode 100644 index 00000000..b6262c41 --- /dev/null +++ b/libs/interval_heap/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "interval_heap" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = { workspace = true } diff --git a/libs/interval_heap/src/lib.rs b/libs/interval_heap/src/lib.rs new file mode 100644 index 00000000..38d3da06 --- /dev/null +++ b/libs/interval_heap/src/lib.rs @@ -0,0 +1,255 @@ +//! # Interval Heaps +//! +//! van Leeuwen, Jan, and Derick Wood. "Interval heaps." The Computer Journal 36.3 (1993): 209-216. +//! +//! +//! * Double-ended priority queue: [`IntervalHeap`] + +/// Interval heap (double-ended priority queue) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IntervalHeap { + values: Vec, +} +impl IntervalHeap { + /// Constructs a new, empty interval heap. + pub fn new() -> Self { + Self::default() + } + + /// Returns $\min(S)$. + pub fn peek_min(&self) -> Option<&T> { + self.values.first() + } + + /// Returns $\max(S)$. + pub fn peek_max(&self) -> Option<&T> { + self.values.get(1).or_else(|| self.values.first()) + } + + /// Removes and returns $\min(S)$. + pub fn pop_min(&mut self) -> Option { + (!self.values.is_empty()).then_some(())?; + let ret = self.values.swap_remove(0); + if self.values.len() >= 2 { + min_heapify_down(&mut self.values, 0); + } + Some(ret) + } + + /// Removes and returns $\max(S)$. + pub fn pop_max(&mut self) -> Option { + if self.values.len() <= 1 { + return self.values.pop(); + } + let ret = self.values.swap_remove(1); + if self.values.len() >= 3 { + max_heapify_down(&mut self.values, 1); + } + Some(ret) + } + + /// $S \leftarrow S \cup \\{\\!\\{x\\}\\!\\}$. + pub fn push(&mut self, x: T) { + self.values.push(x); + let n = self.values.len(); + match n % 2 { + 0 => { + if self.values[n - 2] > self.values[n - 1] { + self.values.swap(n - 2, n - 1); + min_heapify_up(&mut self.values, n - 2); + } else { + max_heapify_up(&mut self.values, n - 1); + } + } + 1 => { + if n == 1 { + return; + } + let end = (n / 2 - 1) | 1; + if self.values[end] < self.values[n - 1] { + self.values.swap(end, n - 1); + max_heapify_up(&mut self.values, end); + } else { + min_heapify_up(&mut self.values, n - 1); + } + } + _ => unreachable!(), + }; + } +} +impl Default for IntervalHeap { + fn default() -> Self { + Self { values: Vec::new() } + } +} +impl From> for IntervalHeap { + fn from(values: Vec) -> Self { + let mut result = Self::new(); + for x in values { + result.push(x); + } + result + } +} + +fn min_heapify_up(values: &mut [T], mut start: usize) { + while start != 0 { + let p = (start / 2 - 1) & !1; + if values[p] <= values[start] { + break; + } + values.swap(p, start); + start = p; + } +} + +fn max_heapify_up(values: &mut [T], mut end: usize) { + while end != 1 { + let p = (end / 2 - 1) | 1; + if values[p] >= values[end] { + break; + } + values.swap(p, end); + end = p; + } +} + +fn min_heapify_down(values: &mut [T], mut start: usize) { + loop { + let end = start + 1; + if end >= values.len() { + break; + } + if values[start] > values[end] { + values.swap(start, end); + } + let left = 2 * start + 2; + if left >= values.len() { + break; + } + let right = 2 * start + 4; + let next = if right >= values.len() || values[left] < values[right] { left } else { right }; + if values[start] <= values[next] { + break; + } + values.swap(start, next); + start = next; + } +} + +fn max_heapify_down(values: &mut [T], mut end: usize) { + let n = values.len(); + loop { + let start = end - 1; + if values[start] > values[end] { + values.swap(start, end); + } + let left = 2 * end + 1; + if left >= n { + break; + } + let right = 2 * end + 3; + let next = if right >= n || values[left] > values[right] { left } else { right }; + if values[end] >= values[next] { + break; + } + values.swap(end, next); + end = next; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::rngs::StdRng; + use rand::Rng; + use rand::SeedableRng; + + fn validate_interval_heap(heap: &IntervalHeap) { + let n = heap.values.len(); + // even is min-heap + { + for i in (0..n / 2).step_by(2) { + let left = 2 * i + 2; + if left < n { + assert!(heap.values[i] <= heap.values[left]); + } + let right = 2 * i + 4; + if right < n { + assert!(heap.values[i] <= heap.values[right]); + } + } + } + // odd is max-heap + { + for i in (1..n / 2).step_by(2) { + let left = 2 * i + 1; + if left < n { + assert!(heap.values[i] >= heap.values[left]); + } + let right = 2 * i + 3; + if right < n { + assert!(heap.values[i] >= heap.values[right]); + } + } + // even <= odd + { + for i in (0..n).step_by(2) { + if i + 1 < n { + assert!(heap.values[i] <= heap.values[i + 1]); + } + } + } + } + } + + #[test] + fn test_interval_heap() { + let mut rng = StdRng::seed_from_u64(42); + for _ in 0..100 { + let n = rng.gen_range(0..4); + let q = rng.gen_range(10..100); + let lim = rng.gen_range(1..=n + q + 10); + let mut vec = (0..n).map(|_| rng.gen_range(0..lim)).collect::>(); + let mut interval_heap = IntervalHeap::from(vec.clone()); + vec.sort_unstable(); + eprintln!("vec = {:?}", vec); + for _ in 0..q { + match rng.gen_range(0..3) { + // push + 0 => { + let x = rng.gen_range(0..lim); + eprintln!("push {}", x); + interval_heap.push(x); + let i = vec.binary_search(&x).unwrap_or_else(|x| x); + vec.insert(i, x); + validate_interval_heap(&interval_heap); + } + // pop_min + 1 => { + eprintln!("pop_min"); + if let Some(x) = interval_heap.pop_min() { + assert_eq!(x, vec.remove(0)); + validate_interval_heap(&interval_heap); + } else { + assert!(vec.is_empty()); + } + } + // pop_max + 2 => { + eprintln!("pop_max"); + if let Some(x) = interval_heap.pop_max() { + assert_eq!(x, vec.pop().unwrap()); + validate_interval_heap(&interval_heap); + } else { + assert!(vec.is_empty()); + } + } + _ => unreachable!(), + } + eprintln!("vec = {:?}", &vec); + } + eprintln!("---"); + } + } +}