[LeetCode] 239. Sliding Window Maximum
You are given an array of integers nums
, there is a sliding window of size k
which is moving from the very left of the array to the very right. You can only see the k
numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3 Output: [3,3,5,5,6,7] Explanation: Window position Max --------------- ----- [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
Example 2:
Input: nums = [1], k = 1 Output: [1]
Example 3:
Input: nums = [1,-1], k = 1 Output: [1,-1]
Example 4:
Input: nums = [9,11], k = 2 Output: [11]
Example 5:
Input: nums = [4,-2], k = 2 Output: [4]
Constraints:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
滑动窗口的最大值。
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我先介绍一下暴力解。这道题的暴力解是创建一个n + 1 - k长度的数组来存放最后的结果。遍历input数组,在每K个数字里面,通过扫描或者优先队列的方式找到这K个数字中间的最大值,然后放到结果集里面。
时间约等于O(n^2) = O(n + 1 - k) * O(nlogk)
空间O(n)
然而这道题的最优解是用到单调栈(monotonic stack),此处引用并提炼了LC中文网的一个discussion,思路如下
处理前 k 个元素,初始化双向队列。
遍历整个数组。在每一步 :
- 清理双向队列,保持队列里面元素数量不多于K个
- 只保留当前滑动窗口中有的元素的索引
- 当遍历到nums[i]的时候,从队列的尾部开始,移除队列中比当前元素小的所有元素,因为它们不可能是最大的
- 将当前元素nums[i]添加到双向队列中,此时nums[i]应该是队列中最小的元素,同时队列应该是递减的
- 将 deque[0] 添加到输出中
- 返回输出数组
作者:LeetCode
链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetcode-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
时间O(n)
空间O(n)
Java实现
1 class Solution { 2 public int[] maxSlidingWindow(int[] nums, int k) { 3 // corner case 4 if (nums == null || nums.length == 0) { 5 return new int[0]; 6 } 7 8 // normal case 9 Deque<Integer> queue = new ArrayDeque<>(); 10 // res长度如何计算?可参考一个例子 11 int[] res = new int[nums.length + 1 - k]; 12 for (int i = 0; i < nums.length; i++) { 13 // 因为queue存的是下标,所以当queue中有K个元素的时候,就可以开始弹出操作了 14 if (!queue.isEmpty() && queue.peekFirst() == i - k) { 15 queue.removeFirst(); 16 } 17 // 单调递增队列的常规操作 18 while (!queue.isEmpty() && nums[queue.peekLast()] < nums[i]) { 19 queue.removeLast(); 20 } 21 queue.offer(i); 22 // 因为队首元素一定是最大的,所以放他 23 // 在res中下标的查找:当前是i,且对于前K个元素,最大元素都是这个,所以下标是i + 1 - k 24 if (i + 1 >= k) { 25 res[i + 1 - k] = nums[queue.peekFirst()]; 26 } 27 } 28 return res; 29 } 30 }
JavaScript实现
1 /** 2 * @param {number[]} nums 3 * @param {number} k 4 * @return {number[]} 5 */ 6 var maxSlidingWindow = function (nums, k) { 7 // corner case 8 if (nums === null || nums.length === 0) { 9 return [0]; 10 } 11 12 // normal case 13 let queue = []; 14 let res = new Array(nums.length + 1 - k); 15 for (let i = 0; i < nums.length; i++) { 16 if (queue.length && queue[0] === i - k) { 17 queue.shift(); 18 } 19 while (queue.length && nums[queue[queue.length - 1]] < nums[i]) { 20 queue.pop(); 21 } 22 queue.push(i); 23 if (i + 1 - k >= 0) { 24 res[i + 1 - k] = nums[queue[0]]; 25 } 26 } 27 return res; 28 };