剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(LeetCode)
基本思路1
维护一个优先队列,保证队首的元素总是队伍中最大的,从左到右遍历数组:
- 如果当前元素的值小于队尾元素的值,那么将当前元素加入到队尾
- 如果当前元素的值大于队尾元素的值,那么将队尾元素进行弹出,再进行比较,直到满足队尾元素大于当前元素,或者队伍为空,将当前元素加入到队尾
!!如果出现当前遍历到的元素大于队尾元素的情况,说明以后窗口的最大值就不可能是该队尾了,所以可以将队尾依次弹出,直到当前元素小于队尾
在遍历的过程中,如果已经完整的遍历过一个窗口(元素下标i >= k-1), 则开始从队首拿出窗口的最大值(由于上述的计算过程,队首肯定是队列中最大的),同时需要考虑当前队首元素是否在当前窗口中,如果不在,则将其从队首移出。
代码1
var maxSlidingWindow = function(nums, k) {
let queue = new Array()
let answer = new Array()
for(let i = 0; i < nums.length; i++){
while(queue.length > 0){
const top = queue.pop()
if(nums[i] <= nums[top]){
queue.push(top)
break
}
}
queue.push(i)
if(i >= k-1){
while(queue.length > 0){
const front = queue.shift()
if(i - front <= k - 1){ // 在范围内
queue.unshift(front)
answer.push(nums[front])
break
}
}
}
}
return answer
};
分析1
空间复杂度: 需要增加一个队列来维护最大值,其中队列最大的长度大概是窗口大小,所以空间复杂度是O(k)
时间复杂度: 外侧需要遍历n次,每次遍历进行的操作次数会有区别,平均下来对于每个元素,都会经历一次入队和出队(不一定)的经历,所以时间复杂度是O(n)
基本思路2
用大根堆(优先队列)来保持堆顶元素最大,滑动窗口时,将新加入的元素加入堆中,此时查看堆顶元素是否在窗口范围内。
- 如果在,堆顶元素就是该窗口下的最大值。
- 如果不在,就一直移除堆顶元素,直到堆顶元素在窗口内,该元素就是该窗口下的最大值。
分析2
空间复杂度: 使用一个数组来模拟堆的结构,为O(n)
时间复杂度: 最坏情况下,数组递增,每次插入元素到堆中,都需要logn的时间,同时有n个数字需要插入,因此是O(nlogn)