Open In App

2D Range Minimum Query in O(1)

Last Updated : 28 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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.


Output

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:



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads