diff --git a/DIRECTORY.md b/DIRECTORY.md index e993c9ae7bd..1a7532b4103 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -65,6 +65,7 @@ * [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/infix_to_postfix.rs) * [Lazy Segment Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/lazy_segment_tree.rs) * [Linked List](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/linked_list.rs) + * [Plus-Minus-One Range Minimum Query](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/plus_minus_rmq.rs) * [Postfix Evaluation](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/postfix_evaluation.rs) * Probabilistic * [Bloom Filter](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/probabilistic/bloom_filter.rs) diff --git a/src/data_structures/mod.rs b/src/data_structures/mod.rs index 660ba8f0608..35a7e97f43c 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -9,6 +9,7 @@ mod heap; mod infix_to_postfix; mod lazy_segment_tree; mod linked_list; +mod plus_minus_rmq; mod postfix_evaluation; mod probabilistic; mod queue; @@ -34,6 +35,7 @@ pub use self::heap::Heap; pub use self::infix_to_postfix::infix_to_postfix; pub use self::lazy_segment_tree::LazySegmentTree; pub use self::linked_list::LinkedList; +pub use self::plus_minus_rmq::PlusMinusOneRMQ; pub use self::postfix_evaluation::evaluate_postfix; pub use self::probabilistic::bloom_filter; pub use self::probabilistic::count_min_sketch; diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs new file mode 100644 index 00000000000..33fec6d157d --- /dev/null +++ b/src/data_structures/plus_minus_rmq.rs @@ -0,0 +1,204 @@ +use super::range_minimum_query::build_sparse_table; +use std::cmp::PartialOrd; + +/// A data-structure for answering +-1 range minimum queries on arrays +/// +/// # Complexity +/// Precomputation in O(n) and queries in O(1) time +/// +/// # Notes +/// This is NOT the general RMQ on arrays but 'just' the +-1 RMQ, which is used in combination with LCA and +/// cartiesian trees on arrays to get a general RMQ implementation +/// +/// # Sources +/// used as reference +pub struct PlusMinusOneRMQ { + array: Vec, + k: usize, + block_min: Vec, + block_min_idx: Vec, + sparse_idx: Vec>, + block_rmq: Vec>>, + block_mask: Vec, +} + +impl PlusMinusOneRMQ { + pub fn new(mut array: Vec) -> Self { + input_padding(&mut array); + let k = (array.len().ilog2() / 2) as usize; + let mut new = Self { + array, + k, + block_min: Vec::new(), + block_min_idx: Vec::new(), + sparse_idx: vec![Vec::new()], // is a sparse table, which only stores the indeces + block_rmq: Vec::new(), + block_mask: Vec::new(), + }; + new.calc_block_min(); + new.sparse_idx = build_sparse_table(&new.array); + new.fill_block_rmq(); + new.precompute_masks(); + + new + } + fn calc_block_min(&mut self) { + for i in 0..(self.array.len() + self.k - 1) / self.k { + let (min, min_idx) = self.calc_min(i * self.k); + self.block_min.push(min); + self.block_min_idx.push(min_idx) + } + } + + fn calc_min(&mut self, i: usize) -> (T, usize) { + let mut current_min = self.array[i]; + let mut min_idx: usize = i; + for j in i..i + self.k { + match self.array.get(j) { + Some(x) => { + current_min = min(current_min, *x); + min_idx = self.min_idx(min_idx, j); + } + None => break, + }; + } + (current_min, min_idx) + } + + pub fn get_range_min(&self, start: usize, end: usize) -> Result { + if start >= end || start >= self.array.len() || end > self.array.len() { + return Err("invalid range"); + } + + let block_l = start / self.k; + let block_r = (end - 1) / self.k; + let l_suffix = self.get_in_block(block_l, start % self.k, self.k - 1); + let r_prefix = self.get_in_block(block_r, 0, (end - 1) % self.k); + match block_r - block_l { + 0 => Ok(self.array[self.get_in_block(block_l, start % self.k, (end - 1) % self.k)]), + 1 => Ok(self.array[self.min_idx(l_suffix, r_prefix)]), + _ => Ok(self.array[self.min_idx( + self.min_idx(l_suffix, self.get_on_blocks(block_l + 1, block_r - 1)), + r_prefix, + )]), + } + } + + fn get_on_blocks(&self, l: usize, r: usize) -> usize { + let loglen = (r - l + 1).ilog2() as usize; + let idx: usize = ((r as i64) - (1 << loglen as i64) + 1) as usize; + let a = self.sparse_idx[loglen][l]; + let b = self.sparse_idx[loglen][idx]; + self.min_idx(a, b) + } + + fn get_in_block(&self, block_idx: usize, l: usize, r: usize) -> usize { + let mask = self.block_mask[block_idx]; + let min_idx = self.block_rmq[mask as usize][l][r]; + min_idx + block_idx * self.k + } + + fn fill_block_rmq(&mut self) { + let mask_amount = 1 << (self.k - 1); + for mask in 0..mask_amount { + let tmp = self.rmq_bitmask(mask as u32); // maybe change to usize + self.block_rmq.push(tmp); + } + } + + fn rmq_bitmask(&mut self, mask: u32) -> Vec> { + let mut rmq_matrix: Vec> = vec![vec![0; self.k]; self.k]; + let list = bitmask_to_array(self.k, mask); + for i in 0..self.k { + for j in i..self.k { + if i == j { + rmq_matrix[i][j] = i; + } else { + let min = list[rmq_matrix[i][j - 1]]; //Do we want range-minimum or range-maximum + if list[j] < min { + rmq_matrix[i][j] = j; + } else { + rmq_matrix[i][j] = rmq_matrix[i][j - 1]; + } + } + } + } + rmq_matrix + } + + fn precompute_masks(&mut self) { + for i in 0..self.block_min.len() { + self.block_mask.push(self.calc_bitmask(i)); + } + } + + // we initialize the mask with k-1 ones + // this is necessary so if blocks are of size < k the bitmask is still correct + fn calc_bitmask(&self, block_idx: usize) -> u32 { + let mut mask: u32 = (1 << (self.k - 1)) - 1; + for i in self.k * block_idx + 1..self.k * (block_idx + 1) { + let last = self.array[i - 1]; + match self.array.get(i) { + Some(&x) => { + if last >= x { + mask -= 1 << (self.k - 1 - (i % self.k)); + } + } + None => break, + }; + } + mask + } + + fn min_idx(&self, i: usize, j: usize) -> usize { + if self.array[i] < self.array[j] { + return i; + } + j + } +} + +// padds the given array to have at least length 4 +// this is needed to have a valid k = 0.5 * log n +fn input_padding(array: &mut Vec) { + while array.len() < 4 { + let last = array[array.len() - 1]; + array.push(last); + } +} + +fn min(a: T, b: T) -> T { + match a < b { + true => a, + _ => b, + } +} + +fn bitmask_to_array(k: usize, mut mask: u32) -> Vec { + let mut list: Vec = vec![0]; + for i in 0..k - 1 { + match mask % 2 { + 1 => list.push(list[i] - 1), + _ => list.push(list[i] + 1), + }; + mask /= 2; + } + list.reverse(); + list +} + +#[cfg(test)] +mod tests { + #[test] + fn simple_query_tests() { + let v1 = vec![1, 2, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, -1]; + let sparse_v1 = super::PlusMinusOneRMQ::new(v1); + + assert_eq!(Ok(2), sparse_v1.get_range_min(1, 6)); + assert_eq!(Ok(1), sparse_v1.get_range_min(0, 10)); + assert_eq!(Ok(-1), sparse_v1.get_range_min(10, 13)); + assert!(sparse_v1.get_range_min(4, 3).is_err()); + assert!(sparse_v1.get_range_min(0, 1000).is_err()); + assert!(sparse_v1.get_range_min(1000, 1001).is_err()); + } +} diff --git a/src/data_structures/range_minimum_query.rs b/src/data_structures/range_minimum_query.rs index 82eeb466d58..60bc8514536 100644 --- a/src/data_structures/range_minimum_query.rs +++ b/src/data_structures/range_minimum_query.rs @@ -52,7 +52,7 @@ impl RangeMinimumQuery { } } -fn build_sparse_table(array: &[T]) -> Vec> { +pub fn build_sparse_table(array: &[T]) -> Vec> { let mut table: Vec> = vec![(0..array.len()).collect()]; let len = array.len();