滑动窗口问题:滑动窗口最大最小值问题

402 阅读2分钟

直接看leetcode239原题

image.png

我们当前主要解决的问题就是当前的滑动窗口内部我们应该存储什么值,存储的形式是怎么样的,从而确保我们每次都能取到最小值或最大值?

在这里引入一个单调队列的概念 首先设置一个空队列,如果我们想要求最大值,我们就必须设计一个单调递减的序列,窗口每次移动一格,同时将最新的数据和序列里面已存在的序列对比(从尾部开始对比),如果队尾值比当前值还小,那将比他小的元素统统移出队列,最后将新元素压入队列尾部。 在窗口移动的同时我们前面最大值的元素可能已经超出的窗口范围,这时我们需要在单调队列中将超出范围的元素从列头移除。 窗口一直移动,我们得到的队列就一直是一个单调递减的序列,确保我们能一直获取到最大值。

这个时候我们又可能有疑问了,单调序列我们存值的话,元素超出的窗口范围时我们怎么能确保移除列头的值是正确的呢?也就是怎么证明移除的值就只自己真正要的值得问题。

这问题在我们生活中就出现过,怎么证明你就是你自己?我们首先想到就是身份证,每个公民都有自己的身份证,带了身份证就能证明自己的身份,那数组里面的值得身份证是什么?那就是他对应的下标,每个值对应的下标是永远不会改变的,至此,我们就得到了我们想要存储的信息,那就是存储数组值得下标,因为用下标我们能推导得到值,但是值推导不出他的真实位置。

以下是具体程序实现

var nums = [1,3,-1,-3,5,3,6,7], k = 3;
        // 窗口是3的滑动,每次输出最大值
        // 声明一个空的单调序列
        var queue = [];
        // 存储结果数组
        var ans = []
        for(let i = 0, len = nums.length; i < len; i++) {
            // 如果单调队列有值,同时队列中最后的值小于当前窗口新加入的值,则队列需要将比他小的值都出列,从而维护序列的单调递减性
            let queueLen
            while ((queueLen = queue.length) && (nums[queue[queueLen - 1]] < nums[i])) {
                queue.pop();
            }
            queue.push(i);
            // 初始化的窗口未加载完成之前,不输出任何值
            if (i + 1 < k) continue;
            // 如果窗口移动的时候,需要将序列中已经超出窗口范围的下标值也从列首中移除
            if ((i - queue[0]) >= k) {
                queue.shift();
            }
            ans.push(nums[queue[0]]);
        }
        console.log(ans);

看了以上代码,如果我们想取每次输出的最小值,就把单调递减序列改为单调递增序列即可,原理一样。