题目:
给定数组arr和整数num,共返回有多少个子数组满足如下情况:
Max(arr[i..j])-min(arr[i..j])<=num
Max(arr[i..j])表示子数组arr[i..j]的最大值,min(arr[i..j])表示子数组arr[i..j]中的最小值。
思路:大数组满足小数组也满足:定义两个指针维持一个动态窗口,一个在前一个在后,前指针右滑表示窗口内进数,后指针右滑表示窗口出数。在一个两个维持窗口最大值和最小值的结构。开始时两个指针都指向第一个元素,窗口内是否满足qmax-qmin<=num,若满足,可以计算当前满足的子数组数,那么就是以后指针开头的到前指针位置的子数组数量,这个数量就是前指针-后指针,前指针继续右滑,在判断时候满足,因为判断的是滑动窗口内最大值与最小值的差,如果这个都满足的话,那么其他值的差也肯定满足,当滑到不满足的时候,那么再向右滑也没有意义了,因为再向右滑,最大值与最小值的差只能变大,不能变小了。所以这里应该让后指针右滑,看是否满足,不知满足再让后指针右滑,否则,计算,前指针右滑。知道前后指针都滑完。
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
int[] arr=getRandomArray(30);
int num=5;
printArray(arr);
System.out.println(getNum(arr, num));
}
//for test
public static int[] getRandomArray(int len){
if (len < 0) {
return null;
}
int[] arr=new int[len];
for (int i=0;i<len;i++){
arr[i]= (int) (Math.random()*10);
}
return arr;
}
//for test
public static void printArray(int[] arr){
if (arr != null) {
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
System.out.println();
}
}
public static int getNum(int[] arr,int num){
if (arr == null || arr.length == 0) {
return 0;
}
LinkedList<Integer> qmin=new LinkedList<Integer>();
LinkedList<Integer> qmax=new LinkedList<Integer>();
int i=0;
int j=0;
int res=0;
while (i < arr.length) {
while (j < arr.length) {
//采用双端队列位置最小值
while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[j]) {
qmin.pollLast();
}
qmin.addLast(j);
//维持最大值
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]) {
qmax.pollLast();
}
qmax.addLast(j);
if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) {
break;
}
j++;
}
//如果最大值是i位置上的,此时左指针向右划,i位置过期,i++,当然如果最大值或最小值
//还是i位置上的值,则抛弃。
if (qmin.peekFirst() == i) {
qmin.pollFirst();
}
if (qmax.peekFirst() == i) {
qmax.pollFirst();
}
//计算以i开头到j位置上的子数组数量,就是j-i
res += j - i;
i++;
}
return res;
}
}