We have briefly discussed sparse table in Range Minimum Query (Square Root Decomposition and Sparse Table)
Sparse table concept is used for fast queries on a set of static data (elements do not change). It does preprocessing so that the queries can be answered efficiently.
Example Problem 1 : Range Minimum Query
We have an array arr[0 . . . n-1]. We need to efficiently find the minimum value from index L (query start) to R (query end) where 0 <= L <= R <= n-1. Consider a situation when there are many range queries.
Example:
Input: arr[] = {7, 2, 3, 0, 5, 10, 3, 12, 18};
query[] = [0, 4], [4, 7], [7, 8]
Output: Minimum of [0, 4] is 0
Minimum of [4, 7] is 3
Minimum of [7, 8] is 12
The idea is to precompute minimum of all subarrays of size 2j where j varies from 0 to Log n. We make a table lookup[i][j] such that lookup[i][j] contains minimum of range starting from i and of size 2j. For example lookup[0][3] contains minimum of range [0, 7] (starting with 0 and of size 23)
How to fill this lookup or sparse table?
The idea is simple, fill in a bottom-up manner using previously computed values. We compute ranges with current power of 2 using values of lower power of two. For example, to find a minimum of range [0, 7] (Range size is a power of 3), we can use the minimum of following two.
a) Minimum of range [0, 3] (Range size is a power of 2)
b) Minimum of range [4, 7] (Range size is a power of 2)
Based on above example, below is formula,
// Minimum 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][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][j-1]

For any arbitrary range [l, R], we need to use ranges which are in powers of 2. The idea is to use the closest power of 2. We always need to do at most one comparison (compare the minimum of two ranges which are powers of 2). One range starts with L and ends with “L + closest-power-of-2”. The other range ends at R and starts with “R – same-closest-power-of-2 + 1”. For example, if the given range is (2, 10), we compare the minimum of two ranges (2, 9) and (3, 10).
Based on above example, below is formula,
// For (2, 10), j = floor(Log2(10-2+1)) = 3
j = floor(Log(R-L+1))
// If lookup[2][3] <= lookup[3][3],
// then RMQ(2, 10) = lookup[2][3]
If lookup[L][j] <= lookup[R-(int)pow(2, j)+1][j]
RMQ(L, R) = lookup[L][j]
// If lookup[2][3] > arr[lookup[3][3],
// then RMQ(2, 10) = lookup[3][3]
Else
RMQ(L, R) = lookup[R-(int)pow(2, j)+1][j]
Since we do only one comparison, the time complexity of query is O(1).
Below is the implementation of the above idea.
C++
#include <bits/stdc++.h>
using namespace std;
#define MAX 500
int lookup[MAX][MAX];
void buildSparseTable( int arr[], int n)
{
for ( int i = 0; i < n; i++)
lookup[i][0] = arr[i];
for ( int j = 1; (1 << j) <= n; j++) {
for ( int i = 0; (i + (1 << j) - 1) < n; i++) {
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];
}
}
}
int query( int L, int R)
{
int j = ( int )log2(R - L + 1);
if (lookup[L][j] <= lookup[R - (1 << j) + 1][j])
return lookup[L][j];
else
return lookup[R - (1 << j) + 1][j];
}
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
import java.io.*;
class GFG {
static int MAX = 500 ;
static int [][]lookup = new int [MAX][MAX];
static void buildSparseTable( int arr[], int n)
{
for ( int i = 0 ; i < n; i++)
lookup[i][ 0 ] = arr[i];
for ( int j = 1 ; ( 1 << j) <= n; j++) {
for ( int i = 0 ; (i + ( 1 << j) - 1 ) < n; i++) {
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 ];
}
}
}
static int query( int L, int R)
{
int j = ( int )Math.log(R - L + 1 );
if (lookup[L][j] <= lookup[R - ( 1 << j) + 1 ][j])
return lookup[L][j];
else
return lookup[R - ( 1 << j) + 1 ][j];
}
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
import math
def buildSparseTable(arr, n):
for i in range ( 0 , n):
lookup[i][ 0 ] = arr[i]
j = 1
while ( 1 << j) < = n:
i = 0
while (i + ( 1 << j) - 1 ) < n:
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
def query(L, R):
j = int (math.log2(R - L + 1 ))
if lookup[L][j] < = lookup[R - ( 1 << j) + 1 ][j]:
return lookup[L][j]
else :
return lookup[R - ( 1 << j) + 1 ][j]
if __name__ = = "__main__" :
a = [ 7 , 2 , 3 , 0 , 5 , 10 , 3 , 12 , 18 ]
n = len (a)
MAX = 500
lookup = [[ 0 for i in range ( MAX )]
for j in range ( MAX )]
buildSparseTable(a, n)
print (query( 0 , 4 ))
print (query( 4 , 7 ))
print (query( 7 , 8 ))
|
C#
using System;
public class GFG {
static int MAX= 500;
static int [,]lookup = new int [MAX, MAX];
static void buildSparseTable( int []arr, int n)
{
for ( int i = 0; i < n; i++)
lookup[i, 0] = arr[i];
for ( int j = 1; (1 << j) <= n; j++) {
for ( int i = 0; (i + (1 << j) - 1) < n; i++) {
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];
}
}
}
static int query( int L, int R)
{
int j = ( int )Math.Log(R - L + 1);
if (lookup[L, j] <= lookup[R - (1 << j) + 1, j])
return lookup[L, j];
else
return lookup[R - (1 << j) + 1, j];
}
static public void Main ()
{
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>
var MAX = 500;
var lookup = Array.from(Array(MAX), ()=>Array(MAX));
function buildSparseTable(arr, n)
{
for ( var i = 0; i < n; i++)
lookup[i][0] = arr[i];
for ( var j = 1; (1 << j) <= n; j++) {
for ( var i = 0; (i + (1 << j) - 1) < n; i++) {
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];
}
}
}
function query(L, R)
{
var j = parseInt(Math.log2(R - L + 1));
if (lookup[L][j] <= lookup[R - (1 << j) + 1][j])
return lookup[L][j];
else
return lookup[R - (1 << j) + 1][j];
}
var a = [7, 2, 3, 0, 5, 10, 3, 12, 18];
var n = a.length;
buildSparseTable(a, n);
document.write( query(0, 4) + "<br>" );
document.write( query(4, 7) + "<br>" );
document.write( query(7, 8));
</script>
|
Time Complexity: O(n*Logn)
Auxiliary Space: O(n*Logn)
So sparse table method supports query operation in O(1) time with O(n Log n) preprocessing time and O(n Log n) space.
Example Problem 2 : Range GCD Query
We have an array arr[0 . . . n-1]. We need to find the greatest common divisor in the range L and R where 0 <= L <= R <= n-1. Consider a situation when there are many range queries
Examples:
Input : arr[] = {2, 3, 5, 4, 6, 8}
queries[] = {(0, 2), (3, 5), (2, 3)}
Output : 1
2
1
We use below properties of GCD:
- GCD function is associative [ GCD(a, b, c) = GCD(GCD(a, b), c) = GCD(a, GCD(b, c))], we can compute GCD of a range using GCDs of subranges.
- If we take GCD of an overlapping range more than once, then it does not change answer. For example GCD(a, b, c) = GCD(GCD(a, b), GCD(b, c)). Therefore like minimum range query problem, we need to do only one comparison to find GCD of given range.
We build a sparse table using the same logic as above. After building the sparse table, we can find all GCDs by breaking given range in powers of 2 and add GCD of every piece to the current answer.
C++
#include <bits/stdc++.h>
using namespace std;
#define MAX 500
int table[MAX][MAX];
void buildSparseTable( int arr[], int n)
{
for ( int i = 0; i < n; i++)
table[i][0] = arr[i];
for ( int j = 1; j <= log2(n); j++)
for ( int i = 0; i <= n - (1 << j); i++)
table[i][j] = __gcd(table[i][j - 1],
table[i + (1 << (j - 1))][j - 1]);
}
int query( int L, int R)
{
int j = ( int )log2(R - L + 1);
return __gcd(table[L][j], table[R - (1 << j) + 1][j]);
}
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, 2) << endl;
cout << query(1, 3) << endl;
cout << query(4, 5) << endl;
return 0;
}
|
Java
import java.util.*;
class GFG
{
static final int MAX = 500 ;
static int [][]table = new int [MAX][MAX];
static void buildSparseTable( int arr[],
int n)
{
for ( int i = 0 ; i < n; i++)
table[i][ 0 ] = arr[i];
for ( int j = 1 ; j <= n; j++)
for ( int i = 0 ; i <= n - ( 1 << j); i++)
table[i][j] = __gcd(table[i][j - 1 ],
table[i + ( 1 << (j - 1 ))][j - 1 ]);
}
static int query( int L, int R)
{
int j = ( int )Math.log(R - L + 1 );
return __gcd(table[L][j],
table[R - ( 1 << j) + 1 ][j]);
}
static int __gcd( int a, int b)
{
return b == 0 ? a : __gcd(b, a % b);
}
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.print(query( 0 , 2 ) + "\n" );
System.out.print(query( 1 , 3 ) + "\n" );
System.out.print(query( 4 , 5 ) + "\n" );
}
}
|
Python3
import math
def buildSparseTable(arr, n):
for i in range ( 0 , n):
table[i][ 0 ] = arr[i]
j = 1
while ( 1 << j) < = n:
i = 0
while i < = n - ( 1 << j):
table[i][j] = math.gcd(table[i][j - 1 ],
table[i + ( 1 << (j - 1 ))][j - 1 ])
i + = 1
j + = 1
def query(L, R):
j = int (math.log2(R - L + 1 ))
return math.gcd(table[L][j],
table[R - ( 1 << j) + 1 ][j])
if __name__ = = "__main__" :
a = [ 7 , 2 , 3 , 0 , 5 , 10 , 3 , 12 , 18 ]
n = len (a)
MAX = 500
table = [[ 0 for i in range ( MAX )]
for j in range ( MAX )]
buildSparseTable(a, n)
print (query( 0 , 2 ))
print (query( 1 , 3 ))
print (query( 4 , 5 ))
|
C#
using System;
class GFG
{
static readonly int MAX = 500;
static int [,]table = new int [MAX, MAX];
static void buildSparseTable( int []arr,
int n)
{
for ( int i = 0; i < n; i++)
table[i, 0] = arr[i];
for ( int j = 1; j <= n; j++)
for ( int i = 0; i <= n - (1 << j); i++)
table[i, j] = __gcd(table[i, j - 1],
table[i + (1 << (j - 1)),
j - 1]);
}
static int query( int L, int R)
{
int j = ( int )Math.Log(R - L + 1);
return __gcd(table[L, j],
table[R - (1 << j) + 1, j]);
}
static int __gcd( int a, int b)
{
return b == 0 ? a : __gcd(b, a % b);
}
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.Write(query(0, 2) + "\n" );
Console.Write(query(1, 3) + "\n" );
Console.Write(query(4, 5) + "\n" );
}
}
|
Javascript
<script>
let MAX = 500;
let table = new Array(MAX);
for (let i = 0; i < MAX; i++)
{
table[i] = new Array(MAX);
for (let j = 0; j < MAX; j++)
table[i][j] = 0;
}
function buildSparseTable(arr,n)
{
for (let i = 0; i < n; i++)
table[i][0] = arr[i];
for (let j = 1; j <= n; j++)
for (let i = 0; i <= n - (1 << j); i++)
table[i][j] = __gcd(table[i][j - 1],
table[i + (1 << (j - 1))][j - 1]);
}
function query(L,R)
{
let j = Math.floor(Math.log(R - L + 1));
return __gcd(table[L][j],
table[R - (1 << j) + 1][j]);
}
function __gcd(a,b)
{
return b == 0 ? a : __gcd(b, a % b);
}
let a = [7, 2, 3, 0, 5, 10, 3, 12, 18];
let n = a.length;
buildSparseTable(a, n);
document.write(query(0, 2) + "<br>" );
document.write(query(1, 3) + "<br>" );
document.write(query(4, 5) + "<br>" );
</script>
|
Time Complexity: O(n*Logn)
Auxiliary Space: O(n*Logn)