给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
提示:
1 <= nums.length <= 10^5
-10⁴ <= nums[i] <= 104⁴
1 <= k <= nums.length
这道题如果用暴力破解的方式比较简单,但是时间复杂度比较高,先说暴力破解的方法。
窗口内的数字数量是已知的k
,可以根据原始数组nums
跟窗口内数字数量k
得出有多少个窗口,也就是返回的数组长度,公式为:nums.length - k + 1
,以示例一代入公式:8 - 3 + 1 = 6
示例一的窗口数量为6个,后面的循环次数用窗口数量即可。
窗口数量已知可能性就已知,所以第一层循环次数直接以nums.length - k + 1
结果的值为判定条件,二层循环则去判定窗口内k
个数值的最大值,取每个窗口的最大值写入到返回数组,这是暴力破解的方式。
代码如下:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int fnum = nums.length-k+1;
int result[] = new int[fnum];
for(int i = 0; i < fnum; i++){
int max = -10001;
for(int j = i;j < k+i; j++){
if(nums[j] > max){
max = nums[j];
}
}
result[i] = max;
}
return result;
}
}
执行结果:
暴力破解的时间复杂度为O(n²)
,空间复杂度为O(1)
,这种方式的解法超出了时间限制,那就得换一种方式了。
既然题目要求的是获取每个窗口的最大值,在循环的时候使用队列
记录窗口循环下标内最大的值,在队列中保证队列是有序的递减队列
,这样获取最大值
直接从队列头
获取即可,如果队列尾值 < 循环值
需要将队列尾值弹出,如果循环下标超出窗口位数要将队列头值弹出。
操作步骤:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
L-R为窗口值
初始状态:L=R=0,队列:{}
i=0,nums[0]=1。队列为空,直接加入。队列:{1}
i=1,nums[1]=3。队尾值为1,3>1,弹出队尾值,加入3。队列:{3}
i=2,nums[2]=-1。队尾值为3,-1<3,直接加入。队列:{3,-1}。此时窗口已经形成,L=0,R=2,result=[3]
i=3,nums[3]=-3。队尾值为-1,-3<-1,直接加入。队列:{3,-1,-3}。队首3对应的下标为1,L=1,R=3,有效。result=[3,3]
i=4,nums[4]=5。队尾值为-3,5>-3,依次弹出后加入。队列:{5}。此时L=2,R=4,有效。result=[3,3,5]
i=5,nums[5]=3。队尾值为5,3<5,直接加入。队列:{5,3}。此时L=3,R=5,有效。result=[3,3,5,5]
i=6,nums[6]=6。队尾值为3,6>3,依次弹出后加入。队列:{6}。此时L=4,R=6,有效。result=[3,3,5,5,6]
i=7,nums[7]=7。队尾值为6,7>6,弹出队尾值后加入。队列:{7}。此时L=5,R=7,有效。result=[3,3,5,5,6,7]
代码如下:
public int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<Integer>();
int result[] = new int[nums.length-k+1];
for (int i = 0; i < nums.length; i++) {
// 判断值是否比尾部值大,大的话将队列的尾部值退出
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.addLast(i);
// 超出窗口的值退出队列
if(deque.peek() <= i-k){
deque.poll();
}
// 前面n-1不参与数组写入
if(i+1 >= k){
result[i+1-k] = nums[deque.peek()];
}
}
return result;
}
执行结果:
这种方法的时间复杂度为O(n)