RMQ
RMQ(Range Minimum Query) 是指区间最值查询,即对于长度为
解决 RMQ 有三种方法:
1. 普通动态规划;
2. Sparse Table(ST);
3. Segment Tree(线段树)。
普通动态规划
用
算法实现
/**
* @param dp 存储 [i, j] 区间最小值的下标索引
* @param A
* @param length 数组长度
*/
public void generaDP(int[][] M, int[] A, int length){
for (int i = 0; i < length; i++){
M[i][i] = i;
}
for (int i = 0; i < length; i++){
for (int j = i + 1; j < length; j++){
M[i][j] = A[M[i][j - 1]] < A[j] ? M[i][j - 1] : j;
}
}
}
Sparse Table(ST)
Sparse Table(ST) 也是一种动态规划的方法,需要维护一个二维数组
为了计算
算法实现:
/**
* @param M 存储 [i, j] 区间最小值的下标索引
* @param A
* @param length 数组长度
*/
public void sparseTable(int[][] M, int[] A, int length){
for (int i = 0; i < length; i++){
M[i][0] = i;
}
for (int j = 1; 1 << j <= length; j++){
for (int i = 0; i + (1 << j) - 1 < length; i++){
M[i][j] = A[M[i][j - 1]] < A[M[i + (1 << j - 1)][j - 1]] ? M[i][j - 1] : M[i + (1 << j - 1)][j - 1];
}
}
}
一旦得到预处理之后的值,就可以计算
RMQ_{A}(i, j) = \left\{\begin{matrix}
M[i][k], & A[M[i][k]] \leqslant A[M[j - 2^{k} + 1][k]] \\
M[j - 2^{k} + 1][k], & otherwise
\end{matrix}\right.
算法实现:
/**
* 用 Sparse Table 求 RMQ
* @param left 查询区间左下标索引
* @param right 查询区间右下标索引
* @param M Sparse Table
* @param A
*/
public void rmqSparseTable(int left, int right, int[][] M, int[] A){
int k = (int) ( Math.log(right - left + 1) / Math.log(2));
int rmq = A[M[left][k]] < A[M[right - (int) Math.pow(2, k) + 1][k]] ? M[left][k] : M[right - (int) Math.pow(2, k) + 1][k];
}
Segment Tree(线段树)
RMQ 也可以用 Segment Tree 来解决,其算法复杂度是
1. 第一个节点存储区间
2. 如果
注意:对于
由于 Segment Tree 是一种类堆的数据结构,所以如果有一个非叶子节点
/**
* 构建线段树
* @param node 当前线段树的根节点下标索引
* @param A 构建线段树的数组
* @param segmentTree 线段树
* [begin, end] 构建线段树的数组的起始下标索引
*/
public void buildSegmentTree(int node, int begin, int end, int[] A, int[] segmentTree){
if (begin == end){
segmentTree[node] = begin;
}
else {
/**
* compute the values in the left and right subtree iteratively
*/
int middle = (begin + end) / 2;
buildSegmentTree(2 * node, begin, middle, A, segmentTree);
buildSegmentTree(2 * node + 1, middle + 1, end, A, segmentTree);
/**
* search for the minimum value in the first and second half of the interval
*/
segmentTree[node] = A[segmentTree[2 * node]] <= A[segmentTree[2 * node + 1]] ? segmentTree[2 * node] : segmentTree[2 * node + 1];
}
}
如果想找到区间
/**
* @param node 当前线段树的根节点下标
* @param A 构建线段树的数组
* @param segmentTree 线段树
* [begin, end] 当前节点所在的区间
* [left, right] 查询区间
*/
public int query(int node, int begin, int end, int left, int rigth, int[] A, int[] segmentTree){
int p1, p2;
/**
* if the current interval doesn't intersect the query interval, return -1
*/
if (left > end || rigth < begin){
return -1;
}
/**
* if the current interval is included the query interval, return segmentTree[node]
*/
if (begin >= left && end <= rigth){
return segmentTree[node];
}
/**
* compute the minimum position in the left and right part of the interval
*/
int middle = (begin + end) / 2;
p1 = query(2 * node, begin, middle, left, rigth, A, segmentTree);
p2 = query(2 * node + 1, middle + 1, end, left, rigth, A, segmentTree);
/**
* return the position where the overall minimum is
*/
if (p1 == -1){
return segmentTree[node] = p2;
}
if (p2 == -1){
return segmentTree[node] = p1;
}
if (A[p1] <= A[p2]){
return segmentTree[node] = p1;
}
return segmentTree[node] = p2;
}
RMQ
public class RMQ {
/**
* @param M 存储 [i, j] 区间最小值的下标索引
* @param A
* @param length 数组长度
*/
public void generalM(int[][] M, int[] A, int length){
for (int i = 0; i < length; i++){
M[i][i] = i;
}
for (int i = 0; i < length; i++){
for (int j = i + 1; j < length; j++){
M[i][j] = A[M[i][j - 1]] < A[j] ? M[i][j - 1] : j;
}
}
System.out.println(M[2][7]);
}
/**
* @param M 存储 [i, j] 区间最小值的下标索引
* @param A
* @param length 数组长度
*/
public void sparseTable(int[][] M, int[] A, int length){
for (int i = 0; i < length; i++){
M[i][0] = i;
}
for (int j = 1; 1 << j <= length; j++){
for (int i = 0; i + (1 << j) - 1 < length; i++){
M[i][j] = A[M[i][j - 1]] < A[M[i + (1 << j - 1)][j - 1]] ? M[i][j - 1] : M[i + (1 << j - 1)][j - 1];
}
}
}
/**
* 用 Sparse Table 求 RMQ
* @param left 查询区间左下标索引
* @param right 查询区间右下标索引
* @param M Sparse Table
* @param A
*/
public void rmqSparseTable(int left, int right, int[][] M, int[] A){
int k = (int) ( Math.log(right - left + 1) / Math.log(2));
int rmq = A[M[left][k]] < A[M[right - (int) Math.pow(2, k) + 1][k]] ? M[left][k] : M[right - (int) Math.pow(2, k) + 1][k];
System.out.println(rmq);
}
/**
* 构建线段树
* @param node 当前线段树的根节点下标索引
* @param A 构建线段树的数组
* @param segmentTree 线段树
* [begin, end] 构建线段树的数组的起始下标索引
*/
public void buildSegmentTree(int node, int begin, int end, int[] A, int[] segmentTree){
if (begin == end){
segmentTree[node] = begin;
}
else {
/**
* compute the values in the left and right subtree iteratively
*/
int middle = (begin + end) / 2;
buildSegmentTree(2 * node, begin, middle, A, segmentTree);
buildSegmentTree(2 * node + 1, middle + 1, end, A, segmentTree);
/**
* search for the minimum value in the first and second half of the interval
*/
segmentTree[node] = A[segmentTree[2 * node]] <= A[segmentTree[2 * node + 1]] ? segmentTree[2 * node] : segmentTree[2 * node + 1];
}
}
/**
* @param node 当前线段树的根节点下标
* @param A 构建线段树的数组
* @param segmentTree 线段树
* [begin, end] 当前节点所在的区间
* [left, right] 查询区间
*/
public int query(int node, int begin, int end, int left, int rigth, int[] A, int[] segmentTree){
int p1, p2;
/**
* if the current interval doesn't intersect the query interval, return -1
*/
if (left > end || rigth < begin){
return -1;
}
/**
* if the current interval is included the query interval, return segmentTree[node]
*/
if (begin >= left && end <= rigth){
return segmentTree[node];
}
/**
* compute the minimum position in the left and right part of the interval
*/
int middle = (begin + end) / 2;
p1 = query(2 * node, begin, middle, left, rigth, A, segmentTree);
p2 = query(2 * node + 1, middle + 1, end, left, rigth, A, segmentTree);
/**
* return the position where the overall minimum is
*/
if (p1 == -1){
return segmentTree[node] = p2;
}
if (p2 == -1){
return segmentTree[node] = p1;
}
if (A[p1] <= A[p2]){
return segmentTree[node] = p1;
}
return segmentTree[node] = p2;
}
public static void main(String[] args){
RMQ rmq = new RMQ();
int[] A = {2, 4, 3, 1, 6, 7, 8, 9, 1, 7};
int[][] matrix = new int[A.length][A.length];
rmq.generalM(matrix, A, A.length);
rmq.sparseTable(matrix, A, A.length);
rmq.rmqSparseTable(7, 9, matrix, A);
int[] segmentTree = new int[A.length * 4 * 4];
rmq.buildSegmentTree(1, 0, A.length - 1, A, segmentTree);
System.out.println(Arrays.toString(segmentTree));
System.out.println(rmq.query(1, 0, A.length - 1, 2, 7, A, segmentTree));
}
}