思路:
在不考虑使用双向链表的情况下,通过上面的例子最直接可以想到的便是暴力解法,即每滑动一次窗口时都遍历窗口内的值并得出其中最大的值加入到数组中,那么每次遍历窗口的时间复杂度为O(k),k为窗口的长度,那么长度为n的数组总共需要的时间复杂度为O((n+1-k)*k)。
这样固然得出结果,但是否可以在将 “获取窗口内最大值” 的时间复杂度从 O(k)降低至O(1)?
如果要降为O(1)的话,即只需进行一次操作即可获取窗口内的最大值,这里我们可以想到维护一个链表来,并且该链表内的大小顺序是从大(队首)到小(队尾)排序的,那么每次只需从该链表中取出队首元素即可。但这里要注意的是,该链表中的元素必须只包含滑动窗口的元素,因此每次滑动窗口时都要对一些元素进行取舍。
既然我们只能从队首取最大值,那么就应该从队尾添加元素,因此我们选择双向链表来存储相关元素。
这里先明确滑动窗口的左右边界,根据数组长度n,和滑动窗口的长度k,可以得出滑动窗口左右边界的取值为:
左边界范围 i∈[1−k,n+1−k],右边界范围 j∈[0,n−1];
接着要明确如何对双向链表添加和删除元素。因为每次滑动窗口时改变的元素只有nums[i-1]和nums[j+1],我们要考虑的就是当前移动窗口的最大元素就是nums[i],而由于移动了窗口,左边界变为了i+1,此时就需要将nums[i]从队首中移除(队首便是最大元素)。
总结一下就是 若 i > 0,并且队首元素deque.peekFirst()==nums[i-1] => deque.pollFirst()
对于移动窗口后进来的nums[j+1],我们要将其放置在链表中合适的位置(即保持链表中的元素始终是递减的),那么只需循环判断nums[j+1]是否大于队尾元素,如果大于的话则将队尾元素弹出,如果找到了小于的情况则跳出循环并将nums[j+1]加入到队尾
总结一下就是:
while (!deque.isEmpty()&&deque.peekLast()