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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.