最大值减去最小值小于或等于num的子数组的数量
给定数组arr和整数num,返回共有多少个子数组满足如下情况:
max(arr[i..j]) - min(arr[i..j]) <= num
要求:O(N)实现。
思路:
使用两个有序队列(相对于有序栈来命名)qmax和qmin,分别维护arr[i..j]的最大值和最小值更新结构。
当子数组a[i..j]向右滑动一个位置变成arr[i..j+1]时,qmax和qmin可以在O(1)时间更新(上面已经分析,平均
来看的确是O(1)),并且可以在O(1)时间得到arr[i..j+1]的最大值和最小值。
步骤是这样的:i,j 从0开始,首先 j 向右滑动,这个过程中维护arr[i..j]的最大值和最小值更新结构,同时观察
(max-min)的值,当其大于num时,停止 j,此时,arr[i..j] 内以 i 为起始满足条件的子数组有 j - i 个;
然后 i 向右滑动一位,继续上诉过程,直到 i 到达数组末尾。
//若L~R满足条件 则在L~R之间 所有以L开头的子数组都满足条件
public int getNum(int[] arr, int num) {
if (arr == null || arr.length == 0) {
return 0;
}
//qmax和qmin,分别维护arr[i..j]的最大值和最小值更新结构
LinkedList<Integer> qmax = new LinkedList<>(); //降序 头最大
LinkedList<Integer> qmin = new LinkedList<>(); //升序 头最小
int L = 0, R = 0;
int res = 0;
while (L < arr.length) {
while (R < arr.length) {
//当前值大于等于队列尾 最大值结构更新
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) {
qmax.pollLast();
}
qmax.addLast(R);
//最小值结构更新
while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[R]) {
qmin.pollLast();
}
qmin.addLast(R);
//不达标情况
if (arr[qmax.peekFirst()] - arr[qmin.peekFirst()] > num) {
break;
}
R++;
}
//判断最大最小值过期了没
if (qmax.peekFirst() == L)
qmax.pollFirst();
if (qmin.peekFirst() == L)
qmin.peekFirst();
res += R - L; //获得所有以L开头的子数组的数量
L++; //换一个开头
}
return res;
}