题目描述
解题思路
还记得59-II.队列的最大值这道题的解法吗?这道题难度是困难 ,实际上理清思路并不难,无非就是滑动窗口+单调双端队列的结合。我们来一步一步拆解一下:
第一步
总思路:我们准备一个临时存放滑动窗口的数组 window, 然后在这个 window 里拿到最大值,最后将每次求的最大值放的 result 数组中。随着 window 的不断滑动,得到最终结果。
第二步
我们接下来只需要考虑两个问题,一是窗口怎么滑动,二是如何在 window 中获取最大值。
(一)窗口怎么滑动?
-
遍历
nums数组,初始化窗口window,将前k-1个元素装进window, 即当i < k-1时,window.push(nums[i]) -
当
i = k时,将window的最后一个元素push进window,这时候 window 就是一个包含 k 个元素的数组了,此时就可以去计算window中的最大值,并将最大值保存到result中 -
滑动窗口,窗口前移,将
window的最后的元素移除(nums[i-k-1]),将新的元素添加到window,重新计算最大值,直到数组nums遍历结束
let result = [];
// 具体实现见下方
let slideWindow = new SlideWindow();
for(let i = 0; i< nums.length; i++){
if(i < k-1){
// 先把窗口前 k - 1 填满
slideWindow.push(nums[i]);
}else{
// 窗口向前滑动,移入新元素
slideWindow.push(nums[i]);
result.add(slideWindow.max());
// 将窗口的后端的元素删除
slideWindow.shift(nums[i-k+1]);
}
}
(二)如何获取 window 中的最大值?
其实就是创建一个单调的双端队列,这个队列里面只放最大值,如果新进的元素比这个队列的最大值小,全部都出队,最后再将这个最大值返回即可。当然,要注意 window 变化时,即 window 出队的时候,需要判断一下出队的值是否是这个最大值,如果是的话,也需要将这个最大值出队,重新计算。
var SlideWindow = function(){
// 用于暂存 window 的元素
this.window = [];
// 用于存放 window 中的最大值
this.queue = [];
}
SlideWindow.prototype.push = function(value){
this.window.push(value);
// 计算 存入 queue 中的值, 利用单调双端队列,不符合的值全部出队
while(this.queue.length && this.queue[0] < value){
this.queue.pop();
}
// 此时,this.queue[0] 即为当前 window 的最大值。
this.queue.push(value)
}
SlideWindow.prototype.pop = function(value){
// 需要判断一下当前值是否在 window 和 queue 中
if(this.window.length && value === this.window[0]){
// 存在,删掉
this.window.shift();
}
if(this.queue.length && value === this.queue[0]){
this.queue.shift();
}
}
SlideWindow.prototype.max = function(){
return this.queue.length ? this.queue[0] : -1;
}
其实,这个 SlideWindow 就是59-II.队列的最大值的解法。
好了,我们把两段代码和起来,就是最终解法啦!
代码
JS
var SlideWindow = function(){
this.window = [];
this.queue = [];
}
SlideWindow.prototype.push = function(val){
while(this.queue.length && this.queue[this.queue.length - 1] < val){
this.queue.pop();
}
this.window.push(val);
this.queue.push(val);
}
SlideWindow.prototype.max = function() {
return this.queue.length ? this.queue[0] : -1;
}
SlideWindow.prototype.pop = function(n){
if(this.window.length && n === this.window[0]){
this.window.shift();
}
if(this.queue。length && n === this.queue[0]){
this.queue.shift();
}
}
var maxSlidingWindow = function(nums, k) {
let result = [];
let slideWindow = new SlideWindow();
for(let i = 0; i < nums.length; i++){
if(i < k - 1){
slideWindow.push(nums[i]);
}else{
slideWindow.push(nums[i]);
result.push(slideWindow.max())
slideWindow.pop(nums[i-k+1]);
}
}
return result;
};
总结
思路就是滑动窗口+单调双端队列。
时间复杂度: O(n)
空间复杂度: O(k), window 和 queue 中的元素个数不会超过 k 。