239.滑动窗口最大值
- 滑动窗口最大值
难度困难290
给定一个数组 nums,有一个大小为 k *的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 *k
个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
进阶:
你能在线性时间复杂度内解决此题吗?
示例:
输入: 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
1.暴力法
思路:我们可以将滑动窗口问题抽象化,一直移动那么最后的数据格式就是一个二维数组 for for 遍历找出每次滑动窗口的最大值存储起来,就可以了。首先是确定滑动窗口的次数。k是窗口的大小,n是长度 k = 3 n = 8 移动了6 size = n - k +1 外层循环是移动次数 内层循环开始条件是 i 随着移动次数增加, k = i 终止条件为 最终移动的次数 i + k 就到数组的最后了。剩下依次判断就可以了。
时间复杂度:O(N*K) 外层k次 内层N次
空间复杂度:O(n-k+1)
public int[] maxSlidingWindow(int[] nums, int k) {
//参数判断
int length = nums.length;
if(length * k == 0){
return new int [0];
}
//窗口移动的次数为length-k+1
int [] array = new int [length-k+1];
for(int i=0;i<length-k+1;i++){
int max = Integer.MIN_VALUE;
//因为窗口每次都要移动 在i的基础上 增加 终止条件是i+k 说明到底
//每一次loop 找出最大值
for(int j=i;j<i+k;j++){
max = Math.max(max,nums[j]);
}
//该层的最大值
array[i] = max;
}
return array;
}
2.双端队列
思路:使用队列进行存储每次窗口的最大值的下标,如果当前值最大 那么之前的所有值就可以清空了
ps : 1 3 4 4最大 所以在这个窗口期中4一直最大 1 3 就可以出队列。
time:O(N) 队列的出队和入队是O(1) 只存在遍历n次数组中的数据
space:O(n)
private ArrayDeque<Integer> deq = new ArrayDeque();//存储下标值 比较大小
private int [] nums;
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || k == 0){ //参数判断
return new int [0];
}
int n = nums.length; this.nums = nums;
//存储最大值的数组
int [] arr = new int [n-k+1]; int maxIndex = 0;
for(int i=0;i<k;i++){
cleanDeq(i,k);
deq.addLast(i);
maxIndex = nums[maxIndex] > nums[i] ? maxIndex : i;
}
arr[0] = nums[maxIndex];
for(int i = k;i<n;i++){
cleanDeq(i,k);
deq.addLast(i);
arr[i-k+1] = nums[deq.getFirst()];
}
return arr;
}
public void cleanDeq(int i,int k){
if(!deq.isEmpty() && deq.getFirst() == i-k){//删除前边数据较小的。或者无用数据
deq.removeFirst();
}
while(!deq.isEmpty() && nums[i] > nums[deq.getLast()]){
//如果当前值大于之前所有的值 则删除
deq.removeLast();
}
}