数列 - 中央値ヒープ

概要

空の数列から始めて次のことが高速に出来る.

  • 値の追加
  • 中央値の取得
  • 中央値の除去(怪しい)
/// Sequence - Median Heap
#[derive(Debug)]
pub struct MedianHeap<T>
where
    T: Ord + Copy,
{
    head: std::collections::BinaryHeap<T>,
    tail: std::collections::BinaryHeap<std::cmp::Reverse<T>>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Median<T> {
    None,
    Just(T),
    Between(T, T),
}
impl<T: Ord + Copy> MedianHeap<T> {
    pub fn new() -> MedianHeap<T> {
        MedianHeap {
            head: std::collections::BinaryHeap::new(),
            tail: std::collections::BinaryHeap::new(),
        }
    }
    pub fn len(&self) -> usize {
        (self.head.len() + self.tail.len()) / 2
    }
    pub fn push(&mut self, x: T) {
        match (self.head.peek(), self.tail.peek()) {
            (None, None) => {
                self.head.push(x);
                self.tail.push(std::cmp::Reverse(x));
            }
            (Some(&a), Some(&std::cmp::Reverse(b))) => {
                if a <= x && x <= b {
                    self.head.push(x);
                    self.tail.push(std::cmp::Reverse(x));
                } else if x < a {
                    self.head.push(x);
                    self.head.push(x);
                    let _ = self.head.pop();
                    self.tail.push(std::cmp::Reverse(a));
                } else {
                    self.tail.push(std::cmp::Reverse(x));
                    self.tail.push(std::cmp::Reverse(x));
                    let _ = self.tail.pop();
                    self.head.push(b);
                }
            }
            _ => {}
        }
    }
    pub fn peek(&self) -> Median<T> {
        let n = self.len();
        if n == 0 {
            Median::None
        } else if n % 2 == 1 {
            Median::Just(*self.head.peek().unwrap())
        } else {
            let a = self.head.peek().unwrap();
            let b = self.tail.peek().unwrap().0;
            Median::Between(*a, b)
        }
    }
    pub fn pop(&mut self) -> Median<T> {
        let n = self.len();
        if n == 0 {
            Median::None
        } else if n % 2 == 1 {
            let a = self.head.pop().unwrap();
            let _ = self.tail.pop();
            Median::Just(a)
        } else {
            let a = self.head.pop().unwrap();
            let b = self.tail.pop().unwrap().0;
            Median::Between(a, b)
        }
    }
}

#[cfg(test)]
mod test_median_heap {
    use crate::sequence::median_heap::*;

    #[test]
    fn test() {
        let mut mh = MedianHeap::new();
        mh.push(0);
        assert_eq!(mh.peek(), Median::Just(0));
        mh.push(2);
        assert_eq!(mh.peek(), Median::Between(0, 2));
        mh.push(1);
        assert_eq!(mh.pop(), Median::Just(1));
        assert_eq!(mh.pop(), Median::Between(0, 2));
    }
}