|
|
package class_2022_04_3_week;
|
|
|
|
|
|
// 小红书
|
|
|
// 3.13 笔试
|
|
|
// 给定一个数组,想随时查询任何范围上的最大值
|
|
|
// 如果只是根据初始数组建立、并且以后没有修改,
|
|
|
// 那么RMQ方法比线段树方法好实现,时间复杂度O(N*logN),额外空间复杂度O(N*logN)
|
|
|
public class Code02_RMQ {
|
|
|
|
|
|
public static class RMQ {
|
|
|
public int[][] max;
|
|
|
|
|
|
// 下标一定要从1开始,没有道理!就是约定俗成!
|
|
|
public RMQ(int[] arr) {
|
|
|
// 长度!
|
|
|
int n = arr.length;
|
|
|
// 2的几次方,可以拿下n
|
|
|
int k = power2(n);
|
|
|
// n*logn
|
|
|
max = new int[n + 1][k + 1];
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
// i 0:从下标i开始,往下连续的2的0次方个数,中,最大值
|
|
|
// 1...1个
|
|
|
// 2...1个
|
|
|
// 3...1个
|
|
|
max[i][0] = arr[i - 1];
|
|
|
}
|
|
|
for (int j = 1; (1 << j) <= n; j++) {
|
|
|
// i...连续的、2的1次方个数,这个范围,最大值
|
|
|
// i...连续的、2的2次方个数,这个范围,最大值
|
|
|
// i...连续的、2的3次方个数,这个范围,最大值
|
|
|
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
|
|
|
// max[10][3]
|
|
|
// 下标10开始,连续的8个数,最大值是多少
|
|
|
// 1) max[10][2]
|
|
|
// 2) max[14][2]
|
|
|
max[i][j] = Math.max(
|
|
|
max[i][j - 1],
|
|
|
max[i + (1 << (j - 1))][j - 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public int max(int l, int r) {
|
|
|
// l...r -> r - l + 1 -> 2的哪个次方最接近它!
|
|
|
int k = power2(r - l + 1);
|
|
|
return Math.max(max[l][k], max[r - (1 << k) + 1][k]);
|
|
|
}
|
|
|
|
|
|
private int power2(int m) {
|
|
|
int ans = 0;
|
|
|
while ((1 << ans) <= (m >> 1)) {
|
|
|
ans++;
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static class Right {
|
|
|
public int[][] max;
|
|
|
|
|
|
public Right(int[] arr) {
|
|
|
int n = arr.length;
|
|
|
max = new int[n + 1][n + 1];
|
|
|
for (int l = 1; l <= n; l++) {
|
|
|
max[l][l] = arr[l - 1];
|
|
|
for (int r = l + 1; r <= n; r++) {
|
|
|
max[l][r] = Math.max(max[l][r - 1], arr[r - 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public int max(int l, int r) {
|
|
|
return max[l][r];
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static int[] randomArray(int n, int v) {
|
|
|
int[] arr = new int[n];
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
arr[i] = (int) (Math.random() * v);
|
|
|
}
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static void main(String[] args) {
|
|
|
int N = 150;
|
|
|
int V = 200;
|
|
|
int testTimeOut = 20000;
|
|
|
int testTimeIn = 200;
|
|
|
System.out.println("测试开始");
|
|
|
for (int i = 0; i < testTimeOut; i++) {
|
|
|
int n = (int) (Math.random() * N) + 1;
|
|
|
int[] arr = randomArray(n, V);
|
|
|
int m = arr.length;
|
|
|
RMQ rmq = new RMQ(arr);
|
|
|
Right right = new Right(arr);
|
|
|
for (int j = 0; j < testTimeIn; j++) {
|
|
|
int a = (int) (Math.random() * m) + 1;
|
|
|
int b = (int) (Math.random() * m) + 1;
|
|
|
int l = Math.min(a, b);
|
|
|
int r = Math.max(a, b);
|
|
|
int ans1 = rmq.max(l, r);
|
|
|
int ans2 = right.max(l, r);
|
|
|
if (ans1 != ans2) {
|
|
|
System.out.println("出错了!");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
System.out.println("测试结束");
|
|
|
}
|
|
|
|
|
|
}
|