题目描述:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2,3,4,2,6,2,5,1} 及滑动窗口的大小w,如果w为3那么一共存在6个滑动窗口:{[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]},他们的最大值分别为{4,4,6,6,6,5}。
时间复杂度为o(n*w)时间复杂度很好实现, 时间复杂度为o(n)的时间复杂度用单调队列实现
//思路,形成一个单调链表,在头部序号距离此时的序号小于等于w数内,在链表个数满w后每个头部元素都是最大值。
//不过期时头部最大值不变,当过期后会被踢出,下一个值充当最大值
public static List<Integer> getMaxWindow(int[] arr, int w){
List<Integer> list = new ArrayList<>();
if(arr.length == 0) return list;
LinkedList<Integer> gmax = new LinkedList<>();
for(int i = 0; i < arr.length; i++){
//该循环形成了一个单调链表,头大于未
while(!gmax.isEmpty() && arr[gmax.getLast()] <= arr[i]){
gmax.pollLast();
}
gmax.addLast(i);
//如果最大值一直停留在滑动窗口前面则说明这个最大值过期,判断依据为 gmax头部序号==i-w;
if(i-w == gmax.peekFirst()){
gmax.pollFirst();
}
//如果gmax中值没有过期,则在大于w后所有头部都是最大值
if(i >= w-1){
list.add(arr[gmax.peekFirst()]);
}
}
return list;
}
2.搞清楚了上述的最大窗口题可以引申出下面的题目
最大值减去最小值小于或等于 k 的子数组数量
给定数组 arr 和整数 k,共返回有多少个子数组满足如下情况:
max(arr[i..j]) - min(arr[i..j]) <= k
max(arr[i..j])表示子数组 arr[i..j]中的最大值,min(arr[i..j])表示子数组 arr[i..j]中的最小值。
思路: 如果对于暴力解法时间复杂度为o(n^2),而同样本问题与上述问题一样相当于W不停变化的一个滑动窗口问题,并且双端队列要位置一个最大队列一个最小队列来求出最大值与最小值的差。
有两个推论在解题的时候用到,
(1)arr[i…j])满足题目条件,则arr[i…j-1])、arr[i…j-2])…也满足条件,即arr[i…j])的子数组满足条件,因为arr[i…j-1])的最大值只可能小于等于arr[i…j]),arr[i…j-1])的最小值只可能大于等于arr[i…j])。
(2)同理arr[i…j])不满足条件,则包含arr[i…j])的子数组都不满足条件
由以上思路,代码实现如下:
public int getMax(int[] arr, int num){
if(arr == null || arr.length == 0) return 0;
LinkedList<Integer> gmax = new LinkedList<>();
LinkedList<Integer> gmin = new LinkedList<>();
int i = 0;
int j = 0;
int res = 0;
//从[0],[1][2]..来判断滑动窗口
while(i < arr.length){
//从[i,0][i,1][i,2]..来判断滑动窗口
while(j < arr.length){
//gmin.isEmpty() 保证队列有值,arr[gmin.peekLast()] != j保证qmin中的最小数不过期,
//如果arr[gmin.peekLast()] == j 说明在arr[i,j]中最小值已经全部加入队列,在arr[i,j]范围
//不变的情况下,不需要改变值
if(gmin.isEmpty() || arr[gmin.peekLast()] != j){
while(!gmin.isEmpty() && arr[gmin.getLast()] >= arr[j]){
gmin.pollLast();
}
gmin.addLast(j);
while(!gmax.isEmpty() && arr[gmax.getLast()] <= arr[j]){
gmax.pollLast();
}
gmax.addLast(j);
}
//第二个推论
if(arr[gmax.getFirst()] - arr[gmin.getFirst()] > num){
break;
}
j++;
}
//用到第一个推论
res += j-i;
//i已经过期,提出i
if(gmax.peekFirst() == i){
gmax.pollFirst();
}
if(gmin.peekFirst() == i){
gmin.pollFirst();
}
i++;
}
return res;
}