这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
本篇带来两道经典的关于滑动窗口的算法题,有兴趣可在控制台跑一跑~
求和的最大值
题目来源:上一篇掘文《温故知新 —— Sliding Window》
比如给定如下数组:[ 5, 7, 1, 4, 3, 6, 2, 9, 2 ],求出 5 个连续元素的最大和是多少?
[5, 7, 1, 4, 3]
是第一组 5 个连续元素,求和是20
,[7, 1, 4, 3, 6]
是第二组 5 个连续元素,求和是21
......这样一直进行下去,最终对比发现 5 个连续元素的最大和是24
,由[4, 3, 6, 2, 9]
组成;
这里给出本瓜的另一种写法,仅供参考:
var maxSlidingWindow = function(nums, k) {
const sum = function (arr) {
return arr.reduce(function(prev, curr, idx, arr){
return prev + curr;
});
};
let slidingWindow=nums.slice(0,k)
let newSlidingWindow=[]
let maxVal;
for(let i=k;i<nums.length;i++){
newSlidingWindow=slidingWindow.slice(1).concat(nums[i])
maxVal=Math.max(sum(slidingWindow),sum(newSlidingWindow))
slidingWindow = newSlidingWindow
}
return maxVal
};
const nums= [ 5, 7, 1, 4, 3, 6, 2, 9, 2 ]
const k=5
maxSlidingWindow(nums,k) // 24
求最大值集合
题目来源:leetcode-239 (复杂)
给你一个整数数组 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
本题在力扣中,难度被标为:复杂,这复杂吗?
- 写一个函数来判断数组中最大的数;
- 初始化窗口,求最大值保存;
- 滑动窗口,再求最大值保存;
- 滑动直至完毕;
本瓜题解:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
if(k===1) return nums; // 特殊情况
let slidingWindow = nums.slice(0,k)
let newSlidingWindow = []
let resArr = []
resArr.push(Math.max(...slidingWindow))
for(let i=k;i<nums.length;i++){
newSlidingWindow = slidingWindow.slice(1).concat(nums[i])
resArr.push(Math.max(...newSlidingWindow))
slidingWindow = newSlidingWindow
}
return resArr
};
结果,当数组长度为 10 w 时,报错 超出时间限制
;
噢!用 Math.max()
来每次从窗口找最大值,时间复杂度是 O(n * k),仍然很大;
窗口固定,求最大值集合 在根本上是 单调队列 的问题!
本瓜录屏了一个非常棒的动画效果解释:
最后 JS 题解:
var maxSlidingWindow = function (nums, k) {
// 队列数组(存放的是元素下标,为了取值方便)
const q = [];
// 结果数组
const ans = [];
for (let i = 0; i < nums.length; i++) {
// 若队列不为空,且当前元素大于等于队尾所存下标的元素,则弹出队尾
while (q.length && nums[i] >= nums[q[q.length - 1]]) {
q.pop();
}
// 入队当前元素下标
q.push(i);
// 判断当前最大值(即队首元素)是否在窗口中,若不在便将其出队
while (q[0] <= i - k) {
q.shift();
}
// 当达到窗口大小时便开始向结果中添加数据
if (i >= k - 1) ans.push(nums[q[0]]);
}
return ans;
};
实际上,滑动窗口还是有很多扩展的空间,即使是窗口滑动,怎么滑,滑动后怎么做,里面就存在很大的解题思路的差异!
以上。
我是掘金安东尼,公众号同名,输出暴露输入,技术洞见生活,下次再会~~