239. 滑动窗口最大值
给你一个整数数组 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
最初的想法是模拟窗口向右移,每次对窗口内元素进行排序,取最大的数。超时了。
var maxSlidingWindow = function(nums, k) {
let res = [], que = [];
for (let i = 0; i <= nums.length; i++) {
if (i < k) {
// 维持窗口长度为k
que.push(nums[i]);
} else {
// 深拷贝
let tem = [...que];
// 排序
tem.sort((a, b) => a - b);
// 记录将当前窗口中最大值
res.push(tem[k - 1]);
// 删除当前窗口最左边的数
que.shift();
// 当前窗口向右移一位
que.push(nums[i]);
}
}
return res;
};
后边看题解后,可以用单调队列(单调队列:从头到尾单调递增或递减)做,维持一个单调递减的队列,队头总是当前窗口最大的值。
对于如何维持一个单调队列,代码中有详细注释,下面模拟过程
单调队列示例:[8, 5, 2, 1]
维护过程:[8, 5, 2, 1], 要添加的新元素 4
第一次:4 > 1, 删除队尾元素删除,操作后队列:[8, 5, 2]
第二次:4 > 2, 删除队尾元素删除,操作后队列:[8, 5]
第三次:4 < 5, 将4添加到队尾,操作后队列:[8, 5, 4];
你会发现队列的长度并不是一定总是等于k,但是注意
// 如果对头元素时上一个窗口值得开头就弹出,更新对列头
if (que[0] === nums[start++])
que.shift();
这一段代码判断如何更新队头值得好好想一想,它可以保证既是队列长度小于等于k,也能取得正确的数存入结果数组。
完整代码
var maxSlidingWindow = function (nums, k) {
let res = [], que = [], start = 0, i = 0;
while (i < k) {
// 初始化长度为k的窗口
add(que, nums[i++]);
}
while (i <= nums.length) {
// 记录当前窗口最大值
res.push(que[0]);
// 更新当前窗口
add(que, nums[i]);
// 如果对头元素时上一个窗口值得开头就弹出,更新对列头
if (que[0] === nums[start++])
que.shift();
i++;
}
return res;
};
// 维持单调队列
function add(que, value) {
// 将要加入的新元素从队尾开始与元素比较
let back = que[que.length - 1];
// 若队尾元素比当前元素小就将队尾元素从队列中删除
while (back !== undefined && back < value) {
// 更新队尾元素
que.pop();
back = que[que.length - 1];
}
// 将新元素加入队列
que.push(value);
}
如果还有小伙伴不懂可以去看看代码随想录,那里有更加详细的解题过程哦!!!