给定一个数组 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 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length
本题是道难题,难点就在于线性复杂度,我一开始没注意,用了优先队列来解本题,一开始先初始化把k个数存到优先队列里,然后每次迭代就把优先队列里最左边的数给取出来,每次都将队列队头的值给放到答案里即可,复杂度是log(N*log(k)) 超时了。
代码如下:
class Solution {
static Comparator<Integer> cmp = new Comparator<Integer>() {
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
};
public int[] maxSlidingWindow(int[] nums, int k) {
Queue<Integer> queue = new PriorityQueue<>(cmp);
int ans [] = new int [nums.length-k+1];
for (int i = 0; i < k; i++){
queue.add(nums[i]);
}
int left = 0, right = k-1;
while (right < nums.length){
ans[left] = queue.peek();
queue.remove(nums[left]);
left++;
right++;
if(right < nums.length)
queue.add(nums[right]);
}
return ans;
}
}
因此得换个思路,看了题解才明白原来在此基础上优化一下即可,用一个双端队列即可。
从左往右遍历数组,维护一个最大大小为k的队列,每次遍历都将数字存到队列尾部,在此之前先将队列里所有小于该数的数字去处,因为以后再也不会用到他们了。这样就能保证每次在队头的一定是最大的元素,并且每次操作经过均摊,实现了O(N)的复杂度。
注意的是这次我们队列里存放的是下标,原因就在于我们需要剔除当队头的元素已经不在滑动窗口中,我们需要用下标进行处理。
代码如下:
class Solution {
int k;
public int[] maxSlidingWindow(int[] nums, int k) {
this.k = k;
ArrayDeque<Integer> deq = new ArrayDeque<>();
int ans [] = new int [nums.length-k+1];
for (int i = 0; i < k; i++){
clear(deq,nums,i);
deq.addLast(i);
}
ans[0] = nums[deq.getFirst()];
for( int i = k; i < nums.length; i++) {
clear(deq,nums,i);
deq.addLast(i);
ans[i-k+1] = nums[deq.getFirst()];
}
return ans;
}
void clear (ArrayDeque<Integer> deq,int[] nums,int i){
while (!deq.isEmpty() && deq.getFirst() + k <= i) deq.removeFirst();
while (!deq.isEmpty() && nums[deq.getLast()] < nums[i]) deq.removeLast();
}
}
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。