diff --git a/src/data_structures/range_minimum_query.rs b/src/data_structures/range_minimum_query.rs index 82eeb466d58..8bb74a7a1fe 100644 --- a/src/data_structures/range_minimum_query.rs +++ b/src/data_structures/range_minimum_query.rs @@ -1,133 +1,194 @@ -/* - A RangeMinimumQuery, is a data structure for answering range-minimum-queries of an array. - For a given array A[], of elements for which an ordering exists, we want to find the - minimum value A[x] of a subarray A[i..j], where i and j are the query parameters. - - Precomputation complexity: O(n log(n)) - Query complexity: O(1) - - Wikipedia: -*/ +//! Range Minimum Query (RMQ) Implementation +//! +//! This module provides an efficient implementation of a Range Minimum Query data structure using a +//! sparse table approach. It allows for quick retrieval of the minimum value within a specified subdata +//! of a given data after an initial preprocessing phase. +//! +//! The RMQ is particularly useful in scenarios requiring multiple queries on static data, as it +//! allows querying in constant time after an O(n log(n)) preprocessing time. +//! +//! References: [Wikipedia](https://en.wikipedia.org/wiki/Range_minimum_query) use std::cmp::PartialOrd; -use std::fmt; -/// Custom error for invalid range -#[derive(Debug, PartialEq)] -pub struct RangeError; - -impl fmt::Display for RangeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Invalid range") - } +/// Custom error type for invalid range queries. +#[derive(Debug, PartialEq, Eq)] +pub enum RangeError { + /// Indicates that the provided range is invalid (start index is not less than end index). + InvalidRange, + /// Indicates that one or more indices are out of bounds for the data. + IndexOutOfBound, } +/// A data structure for efficiently answering range minimum queries on static data. pub struct RangeMinimumQuery { - // the current version makes a copy of the input array, but this could be changed - // to references if needed (in that case, we dont need T to implement the Copy trait) - array: Vec, + /// The original input data on which range queries are performed. + data: Vec, + /// The sparse table for storing preprocessed range minimum information. Each entry + /// contains the index of the minimum element in the range starting at `j` and having a length of `2^i`. sparse_table: Vec>, } impl RangeMinimumQuery { + /// Creates a new `RangeMinimumQuery` instance with the provided input data. + /// + /// # Arguments + /// + /// * `input` - A slice of elements of type `T` that implement `PartialOrd` and `Copy`. + /// + /// # Returns + /// + /// A `RangeMinimumQuery` instance that can be used to perform range minimum queries. pub fn new(input: &[T]) -> RangeMinimumQuery { RangeMinimumQuery { - array: input.to_vec(), + data: input.to_vec(), sparse_table: build_sparse_table(input), } } + /// Retrieves the minimum value in the specified range [start, end). + /// + /// # Arguments + /// + /// * `start` - The starting index of the range (inclusive). + /// * `end` - The ending index of the range (exclusive). + /// + /// # Returns + /// + /// * `Ok(T)` - The minimum value found in the specified range. + /// * `Err(RangeError)` - An error indicating the reason for failure, such as an invalid range + /// or indices out of bounds. pub fn get_range_min(&self, start: usize, end: usize) -> Result { - if start >= end || start >= self.array.len() || end > self.array.len() { - return Err(RangeError); + // Validate range + if start >= end { + return Err(RangeError::InvalidRange); } - let loglen = (end - start).ilog2() as usize; - let idx: usize = end - (1 << loglen); - let a = self.sparse_table[loglen][start]; - let b = self.sparse_table[loglen][idx]; - if self.array[a] < self.array[b] { - return Ok(self.array[a]); + if start >= self.data.len() || end > self.data.len() { + return Err(RangeError::IndexOutOfBound); + } + + // Calculate the log length and the index for the sparse table + let log_len = (end - start).ilog2() as usize; + let idx: usize = end - (1 << log_len); + + // Retrieve the indices of the minimum values from the sparse table + let min_idx_start = self.sparse_table[log_len][start]; + let min_idx_end = self.sparse_table[log_len][idx]; + + // Compare the values at the retrieved indices and return the minimum + if self.data[min_idx_start] < self.data[min_idx_end] { + Ok(self.data[min_idx_start]) + } else { + Ok(self.data[min_idx_end]) } - Ok(self.array[b]) } } -fn build_sparse_table(array: &[T]) -> Vec> { - let mut table: Vec> = vec![(0..array.len()).collect()]; - let len = array.len(); +/// Builds a sparse table for the provided data to support range minimum queries. +/// +/// # Arguments +/// +/// * `data` - A slice of elements of type `T` that implement `PartialOrd`. +/// +/// # Returns +/// +/// A 2D vector representing the sparse table, where each entry contains the index of the minimum +/// element in the range defined by the starting index and the power of two lengths. +fn build_sparse_table(data: &[T]) -> Vec> { + let mut sparse_table: Vec> = vec![(0..data.len()).collect()]; + let len = data.len(); - for loglen in 1..=len.ilog2() { + // Fill the sparse table + for log_len in 1..=len.ilog2() { let mut row = Vec::new(); - for i in 0..=len - (1 << loglen) { - let a = table[table.len() - 1][i]; - let b = table[table.len() - 1][i + (1 << (loglen - 1))]; - if array[a] < array[b] { - row.push(a); + for idx in 0..=len - (1 << log_len) { + let min_idx_start = sparse_table[sparse_table.len() - 1][idx]; + let min_idx_end = sparse_table[sparse_table.len() - 1][idx + (1 << (log_len - 1))]; + if data[min_idx_start] < data[min_idx_end] { + row.push(min_idx_start); } else { - row.push(b); + row.push(min_idx_end); } } - table.push(row); + sparse_table.push(row); } - table + + sparse_table } #[cfg(test)] mod tests { - use super::build_sparse_table; + use super::*; + macro_rules! test_build_sparse_table { ($($name:ident: $inputs:expr,)*) => { - $( - #[test] - fn $name() { - let (array, expected) = $inputs; - assert_eq!(build_sparse_table(&array), expected); - } - )* + $( + #[test] + fn $name() { + let (data, expected) = $inputs; + assert_eq!(build_sparse_table(&data), expected); + } + )* } } + test_build_sparse_table! { - small: ([1, 6, 3], vec![vec![0, 1, 2], vec![0, 2]]), - tc_1: ([1, 3, 6, 123, 7, 235, 3, -4, 6, 2], vec![ - vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - vec![0, 1, 2, 4, 4, 6, 7, 7, 9], - vec![0, 1, 2, 6, 7, 7, 7], - vec![7, 7, 7] - ]), - tc_2: ([ - 20, 13, -13, 2, 3634, -2, 56, 3, 67, 8, 23, 0, -23, 1, 5, 85, 3, 24, 5, -10, 3, 4, 20, - ], vec![ - vec![ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22 - ], - vec![1, 2, 2, 3, 5, 5, 7, 7, 9, 9, 11, 12, 12, 13, 14, 16, 16, 18, 19, 19, 20, 21], - vec![2, 2, 2, 5, 5, 5, 7, 7, 11, 12, 12, 12, 12, 13, 16, 16, 19, 19, 19, 19], - vec![2, 2, 2, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 19, 19, 19], - vec![12, 12, 12, 12, 12, 12, 12, 12] - ]), + small: ( + [1, 6, 3], + vec![ + vec![0, 1, 2], + vec![0, 2] + ] + ), + medium: ( + [1, 3, 6, 123, 7, 235, 3, -4, 6, 2], + vec![ + vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + vec![0, 1, 2, 4, 4, 6, 7, 7, 9], + vec![0, 1, 2, 6, 7, 7, 7], + vec![7, 7, 7] + ] + ), + large: ( + [20, 13, -13, 2, 3634, -2, 56, 3, 67, 8, 23, 0, -23, 1, 5, 85, 3, 24, 5, -10, 3, 4, 20], + vec![ + vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], + vec![1, 2, 2, 3, 5, 5, 7, 7, 9, 9, 11, 12, 12, 13, 14, 16, 16, 18, 19, 19, 20, 21], + vec![2, 2, 2, 5, 5, 5, 7, 7, 11, 12, 12, 12, 12, 13, 16, 16, 19, 19, 19, 19], + vec![2, 2, 2, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 19, 19, 19], + vec![12, 12, 12, 12, 12, 12, 12, 12] + ] + ), } #[test] fn simple_query_tests() { - let v1 = vec![1, 3, 6, 123, 7, 235, 3, -4, 6, 2]; - let sparse_v1 = super::RangeMinimumQuery::new(&v1); - - assert_eq!(Ok(3), sparse_v1.get_range_min(1, 6)); - assert_eq!(Ok(-4), sparse_v1.get_range_min(0, 10)); - assert_eq!(Ok(6), sparse_v1.get_range_min(8, 9)); - 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()); + let rmq = RangeMinimumQuery::new(&[1, 3, 6, 123, 7, 235, 3, -4, 6, 2]); + + assert_eq!(rmq.get_range_min(1, 6), Ok(3)); + assert_eq!(rmq.get_range_min(0, 10), Ok(-4)); + assert_eq!(rmq.get_range_min(8, 9), Ok(6)); + assert_eq!(rmq.get_range_min(4, 3), Err(RangeError::InvalidRange)); + assert_eq!(rmq.get_range_min(0, 1000), Err(RangeError::IndexOutOfBound)); + assert_eq!( + rmq.get_range_min(1000, 1001), + Err(RangeError::IndexOutOfBound) + ); } #[test] fn float_query_tests() { - let sparse_v1 = super::RangeMinimumQuery::new(&[0.4, -2.3, 0.0, 234.22, 12.2, -3.0]); + let rmq = RangeMinimumQuery::new(&[0.4, -2.3, 0.0, 234.22, 12.2, -3.0]); - assert_eq!(Ok(-3.0), sparse_v1.get_range_min(0, 6)); - assert_eq!(Ok(-2.3), sparse_v1.get_range_min(0, 4)); - assert_eq!(Ok(12.2), sparse_v1.get_range_min(3, 5)); - assert_eq!(Ok(0.0), sparse_v1.get_range_min(2, 3)); + assert_eq!(rmq.get_range_min(0, 6), Ok(-3.0)); + assert_eq!(rmq.get_range_min(0, 4), Ok(-2.3)); + assert_eq!(rmq.get_range_min(3, 5), Ok(12.2)); + assert_eq!(rmq.get_range_min(2, 3), Ok(0.0)); + assert_eq!(rmq.get_range_min(4, 3), Err(RangeError::InvalidRange)); + assert_eq!(rmq.get_range_min(0, 1000), Err(RangeError::IndexOutOfBound)); + assert_eq!( + rmq.get_range_min(1000, 1001), + Err(RangeError::IndexOutOfBound) + ); } }