题目链接
题目
解题过程
step1:将数据采用稀疏表进行预处理,时间复杂度O(nlogn)
step2:查找区间内的最值,时间复杂度O(1)
稀疏表预处理讲解
稀疏表ST结合了动态规划和倍增的思想。
定义dp[i,j] 表示[i,i+2^j-1]区间的数据。
而[i,i+2^j-1] 可以均分为 [i,i+2^(j-1)-1] 、 [i+2^(j-1),i+2^(j-1)++2^(j-1)-1]两个区间。
- [
i,i+2^(j-1)-1] = dp[i][j-1] - [
i+2^(j-1),i+2^(j-1)+2^(j-1)-1] = dp[i+2^(j-1)][j-1]
所以: dp[i,j] = dp[i][j-1] + dp[i+2^(j-1)][j-1]
举例说明ST的实现过程:
查找过程
对于任意查找区间[l,r],都可以将其分为满足以下条件的两个区间:
- 两个区间范围 覆盖查找区间。
- 拆分区间刚好在 预处理的dp 中。即,拆分的区间[left_p,right_p] 满足right_p = left_p + 2^m - 1。
如何拆分呢?
找到m,使其满足2^m <= r-l+1 < 2^(m+1)。拆分后的两个区间为:[l,l+2^m-1] 和 [r-2^m+1,r],对应稀疏表的dp[l][m]和dp[r-2^m+1][m].
求出以上两个区间的最值即可得出[l,r]区间的最值。
例如:
[3,8]区间的m=2,其拆分的区间为:[3,6] 和 [5,8], 只需要计算dp[3][2] 和 dp[5][2]的最值即可。
JAVA实现过程
以给出的题目为例,其java实现代码如下:
public int[] maxSlidingWindow(int[] nums, int k) {
// 采用ST预处理数据
int n = logarithm(nums.length);
int[][] dp = new int[nums.length][n + 1];
// 初始数据
for (int i = 0; i < nums.length; i++) {
dp[i][0] = nums[i];
}
// 按照状态转移方程计算
for (int j = 1; j < n + 1; j++) {
for (int i = 0; i < nums.length; i++) {
if (i + power(j - 1) < nums.length) {
dp[i][j] = max(dp[i][j - 1], dp[i + power(j - 1)][j - 1]);
}
}
}
// 查询数据
int kn = logarithm(k);
int[] res = new int[nums.length - k + 1];
for (int l = 0; l < nums.length - k + 1; l++) {
int r = l + k - 1;
// 计算区间结果
res[l] = max(dp[l][kn], dp[r - power(kn) + 1][kn]);
}
return res;
}
/**
* 求2的a次方
*
* @param a a为正整数 或者 0
* @return
*/
public int power(int a) {
int num = 1;
for (int i = 0; i < a; i++) {
num *= 2;
}
return num;
}
/**
* 求两数之间较大值
*
* @param a
* @param b
* @return
*/
public int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}
/**
* 求以2为低,target的对数
*
* @param target
* @return
*/
public int logarithm(int target) {
if (target == 1) {
return 0;
}
int c = 0;
int num = 1;
while (true) {
if (num > target) {
break;
}
num *= 2;
c++;
}
return --c;
}