题目
题目来源:力扣(LeetCode)
给你一个整数数组 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]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
示例 5:
输入:nums = [4,-2], k = 2
输出:[4]
思路分析
这道题可以使用单调队列来解决,我们维护一个单调递减的队列,将当前滑窗中的元素下标 依次加入到队列中。
当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果前者大于等于后者,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。
由于队列中下标对应的元素是严格单调递减的,因此此时队首下标对应的元素就是滑动窗口中的最大值。但与方法一中相同的是,此时的最大值可能在滑动窗口左边界的左侧,并且随着窗口向右移动,它永远不可能出现在滑动窗口中了。因此我们还需要不断从队首弹出元素,直到队首元素在窗口中为止。
为了可以同时弹出队首和队尾的元素,我们需要使用双端队列。满足这种单调性的双端队列一般称作「单调队列」。
/*** @param {number[]} nums* @param {number} k* @return {number[]}*/var maxSlidingWindow = function(nums, k) {const size = nums.length;const queue = [];// 首先找出前 k 个元素中的最大值for (let i = 0; i < k; i++) {// 如果队列不为空且当前考察元素大于等于队尾元素,则将队尾元素移除。// 直到,队列为空或当前考察元素小于新的队尾元素,将当前考察元素添加到队列中while(queue.length && nums[i] >= nums[queue[queue.length - 1]]) {queue.pop();}// 将元素下标存储到队列中queue.push(i)}// 前 k 个元素中的最大值const res = [nums[queue[0]]]// 下标从 k 开始,查找以 k 为大小的窗口内的最大值for (let i = k; i < size; i++) {// 如果队列不为空且当前考察元素大于等于队尾元素,则将队尾元素移除。// 直到,队列为空或当前考察元素小于新的队尾元素,将当前考察元素添加到队列中while(queue.length && nums[i] >= nums[queue[queue.length -1]]) {queue.pop()}// 将元素下标存储到队列中queue.push(i);// 当队首元素的下标小于滑动窗口左侧边界left时// 表示队首元素已经不在滑动窗口内,因此将其从队首移除while (queue[0] <= i - k) {queue.shift()}// 队首元素是该窗口内的最大值res.push(nums[queue[0]])}return res};
