一、窗口的概念
-
l,r指针,一个指向窗口的右边,一个指向窗口的左面。
-
l,r指针只能往右走,不能回退。
-
l不能超过r.
二、窗口内最大值最小值更新结构
就是在窗口进行加数和减少数的时候,通过一个双向列表,配合得出窗口在任何时刻,最大值或者最小值的值是多少。o(1)的时间复杂度
最大值更新结构
链表:左面是头,右面是尾。加入数据是从尾加入进来的。从左往右必须严格的由大到小。(不能有等于,等于跟小于一样的处理。)
最小值更新结构
区别:从左往右由小到大。
三、例题
1.生成窗口最大值数组
滑动窗口的简单应用,最大值更新结构。
代码实现:
public class chuangkou {
public static void main(String[] args) {
int[] a = new int[]{4,3,5,4,3,3,6,7};
System.out.print(Arrays.toString(window(a,3)));
}
private static int[] window(int[] a, int num) {
int l = -1;
int r = 0;
//窗口最大值更新结构
LinkedList<Integer> list = new LinkedList<>();
//保存结果
int index = 0;
int[] array = new int[a.length-num+1];
//左边
while(l<a.length){
//右边
while(r<a.length){
System.out.println("======="+a[r]);
//1。窗口有值了,要进行list的加入更新。
//当list里有值,并且尾部的值小于a[r]当前进入的数出链表,因为是最大值更新结构,要严格的
//从大到小。
while(!list.isEmpty()&&a[list.peekLast()]<=a[r]){
list.pollLast();
}
//2。当没内容了,或者当前值刚好小于链表尾部的值则从尾部加入数据
list.addLast(r);
//3.当窗口里的数等于3则跳出,进行减小窗口的逻辑。
if(r-l==3){
array[index++] = a[list.peekFirst()];
System.out.println(array[index-1]);
break;
}
//4.r右移继续加数
r++;
}
if(r-l<3){
break;
}
//5.当前窗口内容是3,进行数据减少,并更新list
l++;
r++;
if(!list.isEmpty()&&list.peekFirst()<=l){
list.pollFirst();
}
}
return array;
}
}
2.最大值减去最小值小于等于num的子数组数量。
子数组是连续的,当涉及到连续的最大值和最小值的问题,就可以使用滑动窗口的最大值最小值更新结构。
解题思路:
1.当子数组已经满足(最大值-最小值<=num)即便再减数,依然会满足。(减数只会让最大值越小,最小值越大)
2.当子数组不满足(最大值-最小值<=num)即便增加数,依然不满足条件。(减数只会让最大值更大,最小值更小)
3.根据上面的两个条件,我们可以遍历一遍,找到以i开头的所有满足条件的子数组。
注意事项:
-
计算结果不要安排在加数的判断里,这样会导致最后都满足条件的以i开头的所有结果全部漏掉。
-
l,r的初始值,以及++的逻辑位置要确定好。
代码实现:
public static int window2(int[] a,int num){
int l = 0;
int r = 0;//进入第一个数。
LinkedList<Integer> biglist = new LinkedList<>();
LinkedList<Integer> smalllist = new LinkedList<>();
int res = 0;
while(l<a.length){
while(r<a.length){
//1.最大值更新结构的加数逻辑,当list里的值从尾部开始小于等于加入的值则取出。
while(!biglist.isEmpty()&&a[biglist.peekLast()]<=a[r]){
biglist.pollLast();
}
biglist.addLast(r);
//2.最小值更新结构的加数逻辑
while(!smalllist.isEmpty()&&a[smalllist.peekLast()]>=a[r]){
smalllist.pollLast();
}
smalllist.addLast(r);
//3.当刚不满足条件的进行记录,并跳出进行减数操作。
//条件是:最大值-最小值<=num,当不满足的时候,跳出循环
if(a[biglist.peekFirst()]-a[smalllist.peekFirst()]>num){
//计算次数的运算不能写在这里,当最后一波就算都满足要求的时候会没办法进入判断,进入累加。
break;
}
r++;
}
//4.跳出则进入减数逻辑。
//最大值更新结构的减数
if(biglist.peekFirst()<=l){
biglist.pollFirst();
}
//最小值更新结构
if(smalllist.peekFirst()<=l){
smalllist.pollFirst();
}
//计算结果安排在这里。
res += r-l;
l++;
//这块不需要r增加。
}
return res;
}