Given a matrix mat[][] of size N*M, the task is to find the minimum value in a submatrix of the array, defined by the top-left and bottom-right indices of the submatrix for the given queries.
Example:
Input: N = 4, M = 4, mat[][] = { { 5, 8, 2, 4 }, { 7, 2, 9, 1 }, { 1, 4, 7, 3 }, { 3, 5, 6, 8 } }
queries[][] = {{0, 0, 3, 3}, {1, 1, 2, 2}}
Output:
1
2
Explanation:
For first query, top-left corner at (0, 0) and bottom-right corner at (2, 2), which is the entire input matrix. The minimum value in this submatrix is 1.
For second call, the top-left corner at (1, 1) and bottom-right corner at (2, 2). The minimum value in this submatrix is 2.
One solution to this problem is to use a data structure called a sparse table. A sparse table is a data structure that allows you to perform RMQ in O(1) time after O(nmlogn*logm) preprocessing time.
To build a sparse table for a 2D array, you can follow these steps:
- Preprocess the array to calculate the minimum value for each cell and for each submatrix with a size of 2k * 2l, where k and l are non-negative integers.
- Store the minimum values in a 2D array called the sparse table. The size of the sparse table should be n*m*log(n)*log(m).
- Firstly find the largest value of k such that 2k is less than or equal to the width of the submatrix.
- Then, find the largest value of l such that 2l is less than or equal to the height of the submatrix.
- Using these values, you can look up the minimum value in the sparse table and return it as the result.
Here is a brief example of how to implement a 2D range minimum query using a sparse table in Python:
C++
// C++ code to implement the sparse table #include <bits/stdc++.h> using namespace std; const int N = 100; int matrix[N][N]; int table[N][N][( int )(log2(N) + 1)][( int )(log2(N) + 1)]; // Function to build the sparse table void build_sparse_table( int n, int m) { // Copy the values of the original matrix // to the first element of the table for ( int i = 0; i < n; i++) { for ( int j = 0; j < m; j++) { table[i][j][0][0] = matrix[i][j]; } } // Building the table for ( int k = 1; k <= ( int )(log2(n)); k++) { for ( int i = 0; i + (1 << k) - 1 < n; i++) { for ( int j = 0; j + (1 << k) - 1 < m; j++) { table[i][j][k][0] = min( table[i][j][k - 1][0], table[i + (1 << (k - 1))][j][k - 1][0]); } } } for ( int k = 1; k <= ( int )(log2(m)); k++) { for ( int i = 0; i < n; i++) { for ( int j = 0; j + (1 << k) - 1 < m; j++) { table[i][j][0][k] = min( table[i][j][0][k - 1], table[i][j + (1 << (k - 1))][0][k - 1]); } } } for ( int k = 1; k <= ( int )(log2(n)); k++) { for ( int l = 1; l <= ( int )(log2(m)); l++) { for ( int i = 0; i + (1 << k) - 1 < n; i++) { for ( int j = 0; j + (1 << l) - 1 < m; j++) { table[i][j][k][l] = min( min(table[i][j][k - 1][l - 1], table[i + (1 << (k - 1))][j] [k - 1][l - 1]), min(table[i][j + (1 << (l - 1))] [k - 1][l - 1], table[i + (1 << (k - 1))] [j + (1 << (l - 1))][k - 1] [l - 1])); } } } } } // Function to find the maximum value in a submatrix int rmq( int x1, int y1, int x2, int y2) { // log2(x2-x1+1) gives the power of 2 // which is just less than or equal to x2-x1+1 int k = log2(x2 - x1 + 1); int l = log2(y2 - y1 + 1); // Lookup the value from the table which is // the maximum among the 4 submatrices return max(max(table[x1][y1][k][l], table[x2 - (1 << k) + 1][y1][k][l]), max(table[x1][y2 - (1 << l) + 1][k][l], table[x2 - (1 << k) + 1] [y2 - (1 << l) + 1][k][l])); } // Function to solve the queries void solve( int n, int m, vector<vector< int > >& matrix1, int q, vector< int > queries[]) { int i = 0; while (i < n) { int j = 0; while (j < m) { matrix[i][j] = matrix1[i][j]; j++; } i++; } build_sparse_table(n, m); i = 0; while (i < q) { int x1, y1, x2, y2; x1 = queries[i][0]; y1 = queries[i][1]; x2 = queries[i][2]; y2 = queries[i][3]; cout << rmq(x1, y1, x2, y2) << endl; i++; } } // Driver code int main() { int N = 4, M = 4; vector<vector< int > > matrix1 = { { 5, 8, 2, 4 }, { 7, 2, 9, 1 }, { 1, 4, 7, 3 }, { 3, 5, 6, 8 } }; int Q = 2; vector< int > queries[] = { { 0, 0, 3, 3 }, { 1, 1, 2, 2 } }; // Function call solve(N, M, matrix1, Q, queries); return 0; } |
Java
// Java code to implement the sparse table import java.util.Scanner; class GFG { static final int N = 100 ; static int [][] matrix = new int [N][N]; static int [][][][] table = new int [N][N] [( int )(Math.log(N) / Math.log( 2 ) + 1 )] [( int )(Math.log(N) / Math.log( 2 ) + 1 )]; // Function to build the sparse table static void buildSparseTable( int n, int m) { // Copy the values of the original matrix // to the first element of the table for ( int i = 0 ; i < n; i++) { for ( int j = 0 ; j < m; j++) { table[i][j][ 0 ][ 0 ] = matrix[i][j]; } } // Building the table for ( int k = 1 ; k <= ( int )(Math.log(n) / Math.log( 2 )); k++) { for ( int i = 0 ; i + ( 1 << k) - 1 < n; i++) { for ( int j = 0 ; j + ( 1 << k) - 1 < m; j++) { table[i][j][k][ 0 ] = Math.min(table[i][j][k - 1 ][ 0 ], table[i + ( 1 << (k - 1 ))] [j][k - 1 ][ 0 ]); } } } for ( int k = 1 ; k <= ( int )(Math.log(m) / Math.log( 2 )); k++) { for ( int i = 0 ; i < n; i++) { for ( int j = 0 ; j + ( 1 << k) - 1 < m; j++) { table[i][j][ 0 ][k] = Math.min( table[i][j][ 0 ][k - 1 ], table[i][j + ( 1 << (k - 1 ))][ 0 ] [k - 1 ]); } } } for ( int k = 1 ; k <= ( int )(Math.log(n) / Math.log( 2 )); k++) { for ( int l = 1 ; l <= ( int )(Math.log(m) / Math.log( 2 )); l++) { for ( int i = 0 ; i + ( 1 << k) - 1 < n; i++) { for ( int j = 0 ; j + ( 1 << l) - 1 < m; j++) { table[i][j][k][l] = Math.min( Math.min( table[i][j][k - 1 ][l - 1 ], table[i + ( 1 << (k - 1 ))][j] [k - 1 ][l - 1 ]), Math.min( table[i][j + ( 1 << (l - 1 ))] [k - 1 ][l - 1 ], table[i + ( 1 << (k - 1 ))] [j + ( 1 << (l - 1 ))] [k - 1 ][l - 1 ])); } } } } } // Function to find the maximum value in a submatrix static int rmq( int x1, int y1, int x2, int y2) { // log2(x2-x1+1) gives the power of 2 // which is just less than or equal to x2-x1+1 int k = ( int )(Math.log(x2 - x1 + 1 ) / Math.log( 2 )); int l = ( int )(Math.log(y2 - y1 + 1 ) / Math.log( 2 )); // Lookup the value from the table which is // the maximum among the 4 submatrices return Math.max( Math.max(table[x1][y1][k][l], table[x2 - ( 1 << k) + 1 ][y1][k][l]), Math.max(table[x1][y2 - ( 1 << l) + 1 ][k][l], table[x2 - ( 1 << k) + 1 ] [y2 - ( 1 << l) + 1 ][k][l])); } // Function to solve the queries static void solve( int n, int m, int [][] matrix1, int q, int [][] queries) { for ( int i = 0 ; i < n; i++) { for ( int j = 0 ; j < m; j++) { matrix[i][j] = matrix1[i][j]; } } buildSparseTable(n, m); for ( int i = 0 ; i < q; i++) { int x1, y1, x2, y2; x1 = queries[i][ 0 ]; y1 = queries[i][ 1 ]; x2 = queries[i][ 2 ]; y2 = queries[i][ 3 ]; System.out.println(rmq(x1, y1, x2, y2)); } } // Driver code public static void main(String[] args) { int N = 4 , M = 4 ; int [][] matrix1 = { { 5 , 8 , 2 , 4 }, { 7 , 2 , 9 , 1 }, { 1 , 4 , 7 , 3 }, { 3 , 5 , 6 , 8 } }; int Q = 2 ; int [][] queries = { { 0 , 0 , 3 , 3 }, { 1 , 1 , 2 , 2 } }; // Function call solve(N, M, matrix1, Q, queries); } } // This Code is Contributed by Prasad Kandekar(prasad264) |
Python3
# Python code to implement the sparse table import math N = 100 matrix = [[ 0 for j in range (N)] for i in range (N)] table = [[[[ 0 for l in range ( int (math.log2(N)) + 1 )] for k in range ( int (math.log2(N)) + 1 )] for j in range (N)] for i in range (N)] # Function to build the sparse table def build_sparse_table(n, m): # Copy the values of the original matrix # to the first element of the table for i in range (n): for j in range (m): table[i][j][ 0 ][ 0 ] = matrix[i][j] # Building the table for k in range ( 1 , int (math.log2(n)) + 1 ): for i in range (n - ( 1 << k) + 1 ): for j in range (m - ( 1 << k) + 1 ): table[i][j][k][ 0 ] = min ( table[i][j][k - 1 ][ 0 ], table[i + ( 1 << (k - 1 ))][j][k - 1 ][ 0 ]) for k in range ( 1 , int (math.log2(m)) + 1 ): for i in range (n): for j in range (m - ( 1 << k) + 1 ): table[i][j][ 0 ][k] = min ( table[i][j][ 0 ][k - 1 ], table[i][j + ( 1 << (k - 1 ))][ 0 ][k - 1 ]) for k in range ( 1 , int (math.log2(n)) + 1 ): for l in range ( 1 , int (math.log2(m)) + 1 ): for i in range (n - ( 1 << k) + 1 ): for j in range (m - ( 1 << l) + 1 ): table[i][j][k][l] = min ( table[i][j][k - 1 ][l - 1 ], table[i + ( 1 << (k - 1 ))][j][k - 1 ][l - 1 ], table[i][j + ( 1 << (l - 1 ))][k - 1 ][l - 1 ], table[i + ( 1 << (k - 1 ))][j + ( 1 << (l - 1 ))][k - 1 ][l - 1 ] ) # Function to find the maximum value in a submatrix def rmq(x1, y1, x2, y2): # log2(x2-x1+1) gives the power of 2 which is just less than or equal to x2-x1+1 k = int (math.log2(x2 - x1 + 1 )) l = int (math.log2(y2 - y1 + 1 )) # Lookup the value from the table which is the maximum among the 4 submatrices return max ( table[x1][y1][k][l], table[x2 - ( 1 << k) + 1 ][y1][k][l], table[x1][y2 - ( 1 << l) + 1 ][k][l], table[x2 - ( 1 << k) + 1 ][y2 - ( 1 << l) + 1 ][k][l] ) # Function to solve the queries def solve(n, m, matrix1, q, queries): for i in range (n): for j in range (m): matrix[i][j] = matrix1[i][j] build_sparse_table(n, m) for i in range (q): x1, y1, x2, y2 = queries[i] print (rmq(x1, y1, x2, y2)) N = 4 M = 4 matrix1 = [[ 5 , 8 , 2 , 4 ], [ 7 , 2 , 9 , 1 ], [ 1 , 4 , 7 , 3 ], [ 3 , 5 , 6 , 8 ]] Q = 2 queries = [[ 0 , 0 , 3 , 3 ], [ 1 , 1 , 2 , 2 ]] # Function call solve(N, M, matrix1, Q, queries) # This Code is Contributed by sankar. |
C#
// C# code to implement the sparse table using System; public class GFG { const int N = 100; static int [, ] matrix = new int [N, N]; static int [, , , ] table = new int [N, N, ( int )(Math.Log(N) / Math.Log(2) + 1), ( int )(Math.Log(N) / Math.Log(2) + 1)]; // Function to build the sparse table static void buildSparseTable( int n, int m) { // Copy the values of the original matrix // to the first element of the table for ( int i = 0; i < n; i++) { for ( int j = 0; j < m; j++) { table[i, j, 0, 0] = matrix[i, j]; } } // Building the table for ( int k = 1; k <= ( int )(Math.Log(n) / Math.Log(2)); k++) { for ( int i = 0; i + (1 << k) - 1 < n; i++) { for ( int j = 0; j + (1 << k) - 1 < m; j++) { table[i, j, k, 0] = Math.Min(table[i, j, k - 1, 0], table[i + (1 << (k - 1)), j, k - 1, 0]); } } } for ( int k = 1; k <= ( int )(Math.Log(m) / Math.Log(2)); k++) { for ( int i = 0; i < n; i++) { for ( int j = 0; j + (1 << k) - 1 < m; j++) { table[i, j, 0, k] = Math.Min( table[i, j, 0, k - 1], table[i, j + (1 << (k - 1)), 0, k - 1]); } } } for ( int k = 1; k <= ( int )(Math.Log(n) / Math.Log(2)); k++) { for ( int l = 1; l <= ( int )(Math.Log(m) / Math.Log(2)); l++) { for ( int i = 0; i + (1 << k) - 1 < n; i++) { for ( int j = 0; j + (1 << l) - 1 < m; j++) { table[i, j, k, l] = Math.Min( Math.Min( table[i, j, k - 1, l - 1], table[i + (1 << (k - 1)), j, k - 1, l - 1]), Math.Min( table[i, j + (1 << (l - 1)), k - 1, l - 1], table[i + (1 << (k - 1)), j + (1 << (l - 1)), k - 1, l - 1])); } } } } } // Function to find the maximum value in a submatrix static int rmq( int x1, int y1, int x2, int y2) { // log2(x2-x1+1) gives the power of 2 // which is just less than or equal to x2-x1+1 int k = ( int )(Math.Log(x2 - x1 + 1) / Math.Log(2)); int l = ( int )(Math.Log(y2 - y1 + 1) / Math.Log(2)); // Lookup the value from the table which is // the maximum among the 4 submatrices return Math.Max( Math.Max(table[x1, y1, k, l], table[x2 - (1 << k) + 1, y1, k, l]), Math.Max(table[x1, y2 - (1 << l) + 1, k, l], table[x2 - (1 << k) + 1, y2 - (1 << l) + 1, k, l])); } // Function to solve the queries static void solve( int n, int m, int [, ] matrix1, int q, int [, ] queries) { for ( int i = 0; i < n; i++) { for ( int j = 0; j < m; j++) { matrix[i, j] = matrix1[i, j]; } } buildSparseTable(n, m); for ( int i = 0; i < q; i++) { int x1, y1, x2, y2; x1 = queries[i, 0]; y1 = queries[i, 1]; x2 = queries[i, 2]; y2 = queries[i, 3]; Console.WriteLine(rmq(x1, y1, x2, y2)); } } static public void Main() { // Code int N = 4, M = 4; int [, ] matrix1 = { { 5, 8, 2, 4 }, { 7, 2, 9, 1 }, { 1, 4, 7, 3 }, { 3, 5, 6, 8 } }; int Q = 2; int [, ] queries = { { 0, 0, 3, 3 }, { 1, 1, 2, 2 } }; // Function call solve(N, M, matrix1, Q, queries); } } // This code is contributed by karthik. |
Javascript
// Javascript code to implement the sparse table const N = 100; const matrix = new Array(N).fill( null ).map(() => new Array(N).fill(0)); const table = new Array(N).fill( null ).map(() => new Array(N).fill( null ).map(() => new Array(Math.ceil(Math.log2(N) + 1)).fill( null ).map(() => new Array(Math.ceil(Math.log2(N) + 1)).fill(0)))); // Function to build the sparse table function build_sparse_table(n, m) { // Copy the values of the original matrix // to the first element of the table for (let i = 0; i < n; i++) { for (let j = 0; j < m; j++) { table[i][j][0][0] = matrix[i][j]; } } // Building the table for (let k = 1; k <= Math.log2(n); k++) { for (let i = 0; i + (1 << k) - 1 < n; i++) { for (let j = 0; j + (1 << k) - 1 < m; j++) { table[i][j][k][0] = Math.min( table[i][j][k - 1][0], table[i + (1 << (k - 1))][j][k - 1][0] ); } } } for (let k = 1; k <= Math.log2(m); k++) { for (let i = 0; i < n; i++) { for (let j = 0; j + (1 << k) - 1 < m; j++) { table[i][j][0][k] = Math.min( table[i][j][0][k - 1], table[i][j + (1 << (k - 1))][0][k - 1] ); } } } for (let k = 1; k <= Math.log2(n); k++) { for (let l = 1; l <= Math.log2(m); l++) { for (let i = 0; i + (1 << k) - 1 < n; i++) { for (let j = 0; j + (1 << l) - 1 < m; j++) { table[i][j][k][l] = Math.min( Math.min( table[i][j][k - 1][l - 1], table[i + (1 << (k - 1))][j][k - 1][l - 1] ), Math.min( table[i][j + (1 << (l - 1))][k - 1][l - 1], table[i + (1 << (k - 1))][j + (1 << (l - 1))][k - 1][l - 1] ) ); } } } } } // Function to find the maximum value in a submatrix function rmq(x1, y1, x2, y2) { // log2(x2-x1+1) gives the power of 2 // which is just less than or equal to x2-x1+1 let k = Math.ceil(Math.log2(x2 - x1 + 1)); let l = Math.ceil(Math.log2(y2 - y1 + 1)); // Lookup the value from the table which is // the maximum among the 4 submatrices return Math.max(Math.max(table[x1][y1][k][l], table[x2 - (1 << k) + 1][y1][k][l]), Math.max(table[x1][y2 - (1 << l) + 1][k][l], table[x2 - (1 << k) + 1] [y2 - (1 << l) + 1][k][l])); } // Function to solve the queries function solve(n, m, matrix1,q, queries) { let i = 0; while (i < n) { let j = 0; while (j < m) { matrix[i][j] = matrix1[i][j]; j++; } i++; } build_sparse_table(n, m); i = 0; while (i < q) { let x1, y1, x2, y2; x1 = queries[i][0]; y1 = queries[i][1]; x2 = queries[i][2]; y2 = queries[i][3]; console.log(rmq(x1, y1, x2, y2)+ "<br>" ) i++; } } // Driver code const n= 4, m = 4; const matrix1 = [[5, 8, 2, 4], [7, 2, 9, 1], [1, 4, 7, 3], [3, 5, 6, 8]]; const Q = 2; const queries = [[0, 0, 3, 3], [1, 1, 2, 2]]; // Function call solve(n, m, matrix1, Q, queries); // This code is contributed by Vaibhav. |
1 2
Time complexity:
- O(N * M * log(N) * log(M)) (To build sparse table)
- O(1) (For Each Query)
Auxiliary Space: O(N * M * log(N) * log(M))
Related Articles:
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!