A sparse table is a data structure that precomputes summaries of contiguous blocks of lengths 1, 2, 4, 8 and so on in a fixed array. For range minima, it uses two stored values to answer any nonempty interval query in constant time once the endpoints are known. A quant researcher can use it to retrieve the lowest bar low in many different windows of the same frozen price history. Bender and Farach-Colton, The LCA Problem Revisited, §3.
Build from adjacent halves, query from both ends
Use zero-based, half-open ranges , including and excluding . At level , store the minimum of each valid block . Level zero stores the individual values . Each longer block is two adjacent halves, so for the build uses
For a query of length , choose the largest power of two with . Read the length- block starting at and the length- block ending at . Since , the two blocks stay inside the query and leave no gap. They coincide when . The result is . Erickson, Range-Minimum Queries, §1.1, “Sparse Lookup Table”.
For the fictional bar lows USD, the query has five values, so . The blocks and both have minimum 2 USD. The same 2 USD low at index 4 appears in both blocks, and .
![For fictional USD bar lows, query [1, 6) is covered by [1, 5) and [2, 6). Both minima are 2. The shared indices 2 through 4 contribute 14 twice, so adding the block sums gives 40. Disjoint blocks [1, 5) and 5, 6) sum to 26.
Aligning both covers to the same index shows which observations each method counts.
When overlap changes the answer
Minimum is associative, so it can be grouped into blocks, and idempotent, so combining a summary with itself leaves it unchanged. The two blocks put the shared portion next to itself when combined from left to right. Associativity lets you group those two copies, and idempotence reduces them to one. Maximum works for the same reason.
Addition is associative, so the same build can store sums. But adding the two overlapping block totals gives , while the direct sum is 26. The shared indices 2 through 4 contribute an extra . For an associative operation such as addition, you can instead partition the query from left to right using the powers of two in its length. Here , giving and , with total . That disjoint method uses at most blocks. For static sums alone, prefix sums give constant-time queries by subtraction.
What the speed assumes
For an array of length , each of levels holds at most entries and each entry takes constant time to form. This gives build time and storage. The minimum query uses two lookups and one comparison, with obtained in constant time from a precomputed table of lengths. The query bound starts with array indices. When requests arrive as timestamps, the application first has to locate those endpoints in a sorted time index. Erickson, Range-Minimum Queries, §1.1 and footnote 2.
A correction can invalidate every stored block that includes the changed bar. A single level can have a linear number of overlapping blocks through a central index. If historical values change frequently, a segment tree supports point updates and range minima in time per operation. The SelfMadeQuant lesson Tabular and Time-Indexed Data covers timestamp validation and the different aggregation rules for lows and volume. The preview is public and the full lesson requires membership.
Sources
- Michael A. Bender and Martín Farach-Colton, The LCA Problem Revisited (2000), §3. Power-of-two preprocessing and the two-block range-minimum query.
- Jeff Erickson, Range-Minimum Queries (2025), §1.1. Sparse lookup construction, complexity and constant-time calculation of the level index.