滑动窗口最大值

原题地址

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例 1:

输入: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

示例 2:

输入:nums = [1], k = 1
输出:[1]

示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]

示例 4:

输入:nums = [9,11], k = 2
输出:[11]

示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

看了挺多的题解, 还是有点很懵. 研究了一会儿(通过自己实践), 终于弄明白了

解决办法:

基本原理: 如果滑动窗口中的一个数的右边有比它大的数, 那么这个数不是滑动窗口的最大值

因为右边有比它大的数, 所以它现在不是最大值. 滑动时被选为最大值的情况, 只有在比它大的数全都被移除的时候. 但是滑动时左边的数总是比右边的数先被移除, 所以它再也没有机会变成最大值了QwQ

我们尝试把这些不是最大值的数都删除(见下图, 红色为剩余的数, 红框为滑动窗口):

此时发现, 剩余的数构成了一组递减数列, 因为剩下的数右边都比它小(右边有数比它大的都被删除了嘛~)

这时, 滑动窗口的最大值非常好判断了, 就是第一个数.

那么如何使用代码模拟出这种删除了不是最大值的数的数列的状态呢?

答案就是使用队列, 移除队列尾直到队列尾大于新元素, 记录窗口每个元素在数组中的下标, 根据头尾下标差来判断队列头是否应该被移除.

这样就解决了问题.

代码的话因为我还没学Queue, 所以暂时用ArrayList替代(效率有点低, 但是运行的话也是能过的).

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        List<Integer> tab=new ArrayList<>();
        int[] res=new int[nums.length-k+1];
        int i=0,j=0;
        int[] index=new int[20001];
        int offset=10000;
        while(j<k-1){
            while(tab.size()!=0&&tab.get(tab.size()-1)<=nums[j]){
                tab.remove(tab.size()-1);
            }
            tab.add(nums[j]);
            index[nums[j]+offset]=j;
            if(tab.size()==1){
                i=j;
            }
            if(j-i>=k){
                tab.remove(0);
                i=index[tab.get(0)+offset];
            }
            j++;
        }
        while(j<nums.length){
            while(tab.size()!=0&&tab.get(tab.size()-1)<=nums[j]){
                tab.remove(tab.size()-1);
            }
            tab.add(nums[j]);
            index[nums[j]+offset]=j;
            if(tab.size()==1){
                i=j;
            }
            if(j-i>=k){
                tab.remove(0);
                i=index[tab.get(0)+offset];
            }
            res[j-k+1]=tab.get(0);
            j++;
        }
        return res;
    }
}

PS: 写完随笔感觉身体被掏空, 主要是那个叫draw.io的思维导图工具使用方法还不够熟悉. 而且而且, 我感觉有更为直观的工具来表达我的思路(比如动态图之类的), 但是目前我没有找到合适的工具, 那么写题解的事情我觉得可以暂时缓一缓, 等到找到合适的工具了再来, 一来是为了表达更直观, 二来也是为了效率.

嘛~就酱.

posted @ 2021-01-12 01:01  晴影  阅读(168)  评论(0)    收藏  举报