题意:
给你一个整数数组 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 <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
参考文章
思路:
这道题真的很有意思,也是本菜狗在LeetCode做的第一道困难难度的题目。一开始确实是没想出来要怎么做,看了参考文章的动图后恍然大悟,原来是通过维护一个单调队列来完成的。
首先需要区分单调队列和优先级队列,优先级队列其实就是大根堆,在数据结构中就学到了。而单调队列是一个从队头到队尾依次单调递增或递减的队列。
单调队列使用双向队列Deque来实现,因为单调队列同时需要队首和队尾的信息。
在本题中队列是从头到尾依次递减(准确来说是非递增),每次在从队尾插入一个新元素前,需要考虑新元素是否比当前队尾的元素大,如果是,那么需要将当前队尾元素从队尾弹出(否则插入当前元素后不满足队列单调递减),然后再接着拿新元素与新的队尾元素进行比较(不要忘了需要接着比较),直到队列变成空或新元素小于或等于当前队尾元素时才把新元素从队尾插入到队列当中,参考下图来理解(图片转自代码随想录):
而关于获取当前步骤的最大值,就直接返回队首元素,再考虑当前窗口的第一个元素是否与当前队首元素相同,如果是,就把队首元素从队首弹出(因此才说这个单调队列是非递增的,而不是严格单调递减的)。
本题Java代码:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> deque = new LinkedList<>();
int[] ans = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!deque.isEmpty() && nums[i] > deque.peekFirst()) {
deque.pollFirst();
}
deque.offerFirst(nums[i]);
if (i >= k - 1) {
ans[i - k + 1] = deque.peekLast();
if (ans[i - k + 1] == nums[i - k + 1])
deque.pollLast();
}
}
return ans;
}
}