Range maximum query using Sparse Table
Given an array arr[], the task is to answer queries to find the maximum of all the elements in the index range arr[L…R].
Examples:
Input: arr[] = {6, 7, 4, 5, 1, 3}, q[][] = {{0, 5}, {3, 5}, {2, 4}} Output: 7 5 5 Input: arr[] = {3, 34, 1}, q[][] = {{1, 2}} Output: 34
Approach: A similar problem to answer range minimum queries has been discussed here. The same approach can be modified to answer range maximum queries. Below is the modification:
// Maximum of single element subarrays is same // as the only element lookup[i][0] = arr[i] // If lookup[0][2] ≥ lookup[4][2], // then lookup[0][3] = lookup[0][2] If lookup[i][j-1] ≥ lookup[i+2j-1-1][j-1] lookup[i][j] = lookup[i][j-1] // If lookup[0][2] < lookup[4][2], // then lookup[0][3] = lookup[4][2] Else lookup[i][j] = lookup[i+2j-1-1][j-1]
Below is the implementation of the above approach:
C++
// C++ implementation of the approach #include <bits/stdc++.h> using namespace std; #define MAX 500 // lookup[i][j] is going to store maximum // value in arr[i..j]. Ideally lookup table // size should not be fixed and should be // determined using n Log n. It is kept // constant to keep code simple. int lookup[MAX][MAX]; // Fills lookup array lookup[][] in bottom up manner void buildSparseTable( int arr[], int n) { // Initialize M for the intervals with length 1 for ( int i = 0; i < n; i++) lookup[i][0] = arr[i]; // Compute values from smaller to bigger intervals for ( int j = 1; (1 << j) <= n; j++) { // Compute maximum value for all intervals with // size 2^j for ( int i = 0; (i + (1 << j) - 1) < n; i++) { // For arr[2][10], we compare arr[lookup[0][7]] // and arr[lookup[3][10]] if (lookup[i][j - 1] > lookup[i + (1 << (j - 1))][j - 1]) lookup[i][j] = lookup[i][j - 1]; else lookup[i][j] = lookup[i + (1 << (j - 1))][j - 1]; } } } // Returns maximum of arr[L..R] int query( int L, int R) { // Find highest power of 2 that is smaller // than or equal to count of elements in given // range // For [2, 10], j = 3 int j = ( int )log2(R - L + 1); // Compute maximum of last 2^j elements with first // 2^j elements in range // For [2, 10], we compare arr[lookup[0][3]] and // arr[lookup[3][3]] if (lookup[L][j] >= lookup[R - (1 << j) + 1][j]) return lookup[L][j]; else return lookup[R - (1 << j) + 1][j]; } // Driver program int main() { int a[] = { 7, 2, 3, 0, 5, 10, 3, 12, 18 }; int n = sizeof (a) / sizeof (a[0]); buildSparseTable(a, n); cout << query(0, 4) << endl; cout << query(4, 7) << endl; cout << query(7, 8) << endl; return 0; } |
Java
// Java implementation of the approach import java.util.*; class GFG { static final int MAX = 500 ; // lookup[i][j] is going to store maximum // value in arr[i..j]. Ideally lookup table // size should not be fixed and should be // determined using n Log n. It is kept // constant to keep code simple. static int lookup[][] = new int [MAX][MAX]; // Fills lookup array lookup[][] in bottom up manner static void buildSparseTable( int arr[], int n) { // Initialize M for the intervals with length 1 for ( int i = 0 ; i < n; i++) lookup[i][ 0 ] = arr[i]; // Compute values from smaller to bigger intervals for ( int j = 1 ; ( 1 << j) <= n; j++) { // Compute maximum value for all intervals with // size 2^j for ( int i = 0 ; (i + ( 1 << j) - 1 ) < n; i++) { // For arr[2][10], we compare arr[lookup[0][7]] // and arr[lookup[3][10]] if (lookup[i][j - 1 ] > lookup[i + ( 1 << (j - 1 ))][j - 1 ]) lookup[i][j] = lookup[i][j - 1 ]; else lookup[i][j] = lookup[i + ( 1 << (j - 1 ))][j - 1 ]; } } } // Returns maximum of arr[L..R] static int query( int L, int R) { // Find highest power of 2 that is smaller // than or equal to count of elements in given // range // For [2, 10], j = 3 int j = ( int )Math.log(R - L + 1 ); // Compute maximum of last 2^j elements with first // 2^j elements in range // For [2, 10], we compare arr[lookup[0][3]] and // arr[lookup[3][3]] if (lookup[L][j] >= lookup[R - ( 1 << j) + 1 ][j]) return lookup[L][j]; else return lookup[R - ( 1 << j) + 1 ][j]; } // Driver program public static void main(String args[]) { int a[] = { 7 , 2 , 3 , 0 , 5 , 10 , 3 , 12 , 18 }; int n = a.length; buildSparseTable(a, n); System.out.println(query( 0 , 4 )); System.out.println(query( 4 , 7 )); System.out.println(query( 7 , 8 )); } } |
Python3
# Python3 implementation of the approach from math import log MAX = 500 # lookup[i][j] is going to store maximum # value in arr[i..j]. Ideally lookup table # size should not be fixed and should be # determined using n Log n. It is kept # constant to keep code simple. lookup = [[ 0 for i in range ( MAX )] for i in range ( MAX )] # Fills lookup array lookup[][] # in bottom up manner def buildSparseTable(arr, n): # Initialize M for the intervals # with length 1 for i in range (n): lookup[i][ 0 ] = arr[i] # Compute values from smaller # to bigger intervals i, j = 0 , 1 while ( 1 << j) < = n: # Compute maximum value for # all intervals with size 2^j while (i + ( 1 << j) - 1 ) < n: # For arr[2][10], we compare arr[lookup[0][7]] # and arr[lookup[3][10]] if (lookup[i][j - 1 ] > lookup[i + ( 1 << (j - 1 ))][j - 1 ]): lookup[i][j] = lookup[i][j - 1 ] else : lookup[i][j] = lookup[i + ( 1 << (j - 1 ))][j - 1 ] i + = 1 j + = 1 # Returns maximum of arr[L..R] def query(L, R): # Find highest power of 2 that is smaller # than or equal to count of elements in given # range # For [2, 10], j = 3 j = int (log(R - L + 1 )) # Compute maximum of last 2^j elements with first # 2^j elements in range # For [2, 10], we compare arr[lookup[0][3]] and # arr[lookup[3][3]] if (lookup[L][j] > = lookup[R - ( 1 << j) + 1 ][j]): return lookup[L][j] else : return lookup[R - ( 1 << j) + 1 ][j] # Driver Code a = [ 7 , 2 , 3 , 0 , 5 , 10 , 3 , 12 , 18 ] n = len (a) buildSparseTable(a, n); print (query( 0 , 4 )) print (query( 4 , 7 )) print (query( 7 , 8 )) # This code is contributed by Mohit Kumar |
C#
// Java implementation of the approach using System; class GFG { static int MAX = 500; // lookup[i][j] is going to store maximum // value in arr[i..j]. Ideally lookup table // size should not be fixed and should be // determined using n Log n. It is kept // constant to keep code simple. static int [, ] lookup = new int [MAX, MAX]; // Fills lookup array lookup[][] in bottom up manner static void buildSparseTable( int [] arr, int n) { // Initialize M for the intervals with length 1 for ( int i = 0; i < n; i++) lookup[i, 0] = arr[i]; // Compute values from smaller to bigger intervals for ( int j = 1; (1 << j) <= n; j++) { // Compute maximum value for all intervals with // size 2^j for ( int i = 0; (i + (1 << j) - 1) < n; i++) { // For arr[2][10], we compare arr[lookup[0][7]] // and arr[lookup[3][10]] if (lookup[i, j - 1] > lookup[i + (1 << (j - 1)), j - 1]) lookup[i, j] = lookup[i, j - 1]; else lookup[i, j] = lookup[i + (1 << (j - 1)), j - 1]; } } } // Returns maximum of arr[L..R] static int query( int L, int R) { // Find highest power of 2 that is smaller // than or equal to count of elements in given // range // For [2, 10], j = 3 int j = ( int )Math.Log(R - L + 1); // Compute maximum of last 2^j elements with first // 2^j elements in range // For [2, 10], we compare arr[lookup[0][3]] and // arr[lookup[3][3]] if (lookup[L, j] >= lookup[R - (1 << j) + 1, j]) return lookup[L, j]; else return lookup[R - (1 << j) + 1, j]; } // Driver program public static void Main(String[] args) { int [] a = { 7, 2, 3, 0, 5, 10, 3, 12, 18 }; int n = a.Length; buildSparseTable(a, n); Console.WriteLine(query(0, 4)); Console.WriteLine(query(4, 7)); Console.WriteLine(query(7, 8)); } } |
Javascript
<script> // Javascript implementation of the approach let MAX = 500; // lookup[i][j] is going to store maximum // value in arr[i..j]. Ideally lookup table // size should not be fixed and should be // determined using n Log n. It is kept // constant to keep code simple. let lookup = new Array(); for (let i = 0; i < MAX; i++) { let temp = []; for (let j = 0; j < MAX; j++) { temp.push([]) } lookup.push(temp) } // Fills lookup array lookup[][] in bottom up manner function buildSparseTable(arr, n) { // Initialize M for the letervals with length 1 for (let i = 0; i < n; i++) lookup[i][0] = arr[i]; // Compute values from smaller to bigger letervals for (let j = 1; (1 << j) <= n; j++) { // Compute maximum value for all letervals with // size 2^j for (let i = 0; (i + (1 << j) - 1) < n; i++) { // For arr[2][10], we compare arr[lookup[0][7]] // and arr[lookup[3][10]] if (lookup[i][j - 1] > lookup[i + (1 << (j - 1))][j - 1]) lookup[i][j] = lookup[i][j - 1]; else lookup[i][j] = lookup[i + (1 << (j - 1))][j - 1]; } } } // Returns maximum of arr[L..R] function query(L, R) { // Find highest power of 2 that is smaller // than or equal to count of elements in given // range // For [2, 10], j = 3 let j = Math.floor(Math.log2(R - L + 1)); // Compute maximum of last 2^j elements with first // 2^j elements in range // For [2, 10], we compare arr[lookup[0][3]] and // arr[lookup[3][3]] if (lookup[L][j] >= lookup[R - (1 << j) + 1][j]) return lookup[L][j]; else return lookup[R - (1 << j) + 1][j]; } // Driver program let a = [ 7, 2, 3, 0, 5, 10, 3, 12, 18 ]; let n = a.length; buildSparseTable(a, n); document.write(query(0, 4) + "<br>" ); document.write(query(4, 7) + "<br>" ); document.write(query(7, 8) + "<br>" ); // This code is contributed by _saurabh_jaiswal. </script> |
Output:
7 12 18
So sparse table method supports query operation in O(1) time with O(n Log n) preprocessing time and O(n Log n) space.