Fundamentals 7 min read

Range Maximum Query Using Sparse Table – Theory, Preprocessing, and C++ Implementation

This article explains how to answer range maximum queries on a static array efficiently by preprocessing with a sparse table, achieving O(n log n) build time and O(1) query time, and provides a complete C++ implementation with state compression and interval decomposition.

IT Services Circle
IT Services Circle
IT Services Circle
Range Maximum Query Using Sparse Table – Theory, Preprocessing, and C++ Implementation

The problem is to find the maximum value within any given interval of an array of N numbers, where a naive linear scan would take O(n) time per query.

Because the array does not change, one might consider precomputing results for all intervals, but that requires O(n²) time and space, which is impractical for large N.

The article introduces the sparse table technique, an offline preprocessing method that builds a table in O(n log n) time and answers each query in O(1) time. The table stores the maximum for intervals whose lengths are powers of two.

Define f[i][j] as the maximum value in the interval starting at index i with length 2^j. The recurrence relation is f[i][j] = max(f[i][j‑1], f[i + 2^{j‑1}][j‑1]), allowing the table to be filled by increasing j.

State compression is achieved by using the two dimensions (starting index i and exponent j) directly, reducing memory usage while preserving all needed information.

To answer a query for interval [a, b], compute j = ⌊log₂(b‑a+1)⌋. The answer is max(f[a][j], f[b‑2^j+1][j]), which combines two pre‑computed power‑of‑two intervals that together cover [a, b].

The article provides a complete C++ implementation. First, the arrays are declared:

int high[50000][17], low[50000][17], n, q;

Then the preprocessing function:

void solve()
{
    for (int j = 1; (1 << j) <= n; ++j) {
        for (int i = 0; i + (1 << j) <= n; ++i) {
            high[i][j] = max(high[i][j-1], high[i + (1 << (j-1))][j-1]);
            low[i][j]  = min(low[i][j-1],  low[i + (1 << (j-1))][j-1]);
        }
    }
}

And the main function that reads input, builds the table, and processes queries:

int main()
{
    cin >> n >> q;
    for (int i = 0; i < n; ++i) {
        cin >> high[i][0];
        low[i][0] = high[i][0];
    }
    solve();
    for (int i = 0; i < q; ++i) {
        int a, b; cin >> a >> b; --a; --b;
        int j = (int)(log(b - a + 1.0) / log(2.0));
        int minHeight = min(low[a][j], low[b - (1 << j) + 1][j]);
        int maxHeight = max(high[a][j], high[b - (1 << j) + 1][j]);
        cout << maxHeight - minHeight << endl;
    }
    return 0;
}

In conclusion, the sparse table provides a highly efficient solution for static range maximum (or minimum) queries, leveraging preprocessing, state compression, and dynamic programming; for mutable data, a segment tree or other online structure would be required.

algorithmC++preprocessingrange queryRMQsparse table
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.