滑动窗口的最大值[困难][队列\滑动窗口]

248 阅读2分钟

剑指 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)