Introduction
The Range Minimum Query (RMQ) problem asks: given an array and multiple queries, find the minimum element in a specified range for each query.
Naive approach: For each query, scan from to and find the minimum. This takes per query, or total for queries.
Challenge: With , the naive approach is too slow. We need or per query.
Applications:
- Finding lowest common ancestor (LCA) in trees
- Suffix array construction
- Competitive programming (frequent query pattern)
- Database query optimization
Sparse Table Concept
A Sparse Table preprocesses the array so each query can be answered in time.
Key Insight: Any range can be covered by at most 2 overlapping ranges whose lengths are powers of 2.
For minimum queries, overlapping is fine because .
Table Definition: Let minimum of the range starting at index with length .
Range Coverage: For a query :
- Find the largest such that
- The range is covered by and
- Answer =
These two ranges overlap but together cover completely.
Building the Table
Preprocessing:
Build the sparse table using dynamic programming:
function build_sparse_table(arr):
n = length(arr)
LOG = floor(log2(n)) + 1
// Base case: ranges of length 1
for i from 0 to n-1:
sparse[i][0] = arr[i]
// Fill table for increasing powers of 2
for j from 1 to LOG-1:
for i from 0 to n - 2^j:
sparse[i][j] = min(sparse[i][j-1],
sparse[i + 2^(j-1)][j-1])
Recurrence Explanation: A range of length is the minimum of two adjacent ranges of length :
Space:
Answering Queries
Query:
function query(l, r):
len = r - l + 1
k = floor(log2(len))
return min(sparse[l][k], sparse[r - 2^k + 1][k])
Example: Array , query (0-indexed: )
- Length =
- (since )
- Left range:
- Right range:
- Answer =
Precompute logs: To avoid computing each query, precompute an array:
log[1] = 0
for i from 2 to n:
log[i] = log[i/2] + 1