滑动窗口的最大值

一、需求

  • 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

二、双指针 

2.1  思路分析

  1. 利用双指针分别指向窗口的左右边界,利用一个函数计算边界内的最大值,将返回值存放到结果数组中;

2.2  代码实现

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0) return new int[0];
        //res的长度画个图就能算出来
        int[] res = new int[nums.length-k+1];
        int i = 0;
        int j = k - 1;
        int index = 0;
        while(j < nums.length) {
            res[index] = maxValue(nums, i, j);
            i++;
            j++;
            index++;
        }
        return res;
    }
    //该方法求窗口的的最大值
    public int maxValue(int[] nums, int i, int j) {
        int max = Integer.MIN_VALUE;
        for(int k = i; k <= j; k++) {
            max = Math.max(max, nums[k]);
        }
        return max;
    }
}

2.3  复杂度分析

  • 时间复杂度为O(NK),其中N为数组元素的个数,滑动窗口要滑动N-1+k次,每次查找最大值需要K次,故总体时间复杂度为O(NK);
  • 空间复杂度为O(N),每次滑动都要存储一个最大值,故总体消耗O(N)级别的空间;

三、单调队列

3.1  思路分析

  1. 我们使用一个非严格递减的双端队列,其中队列中只包含窗口中的数据,队头始终为窗口里元素的最大值;
  2. 使用变量 i , j 来表示窗口的左、右边界,一开始 i 初始化为 1-k,j 初始化为0,即 0 - (1 - k) + 1 == k,就是窗口的大小;
  3. 然后窗口就开始向右滑动,当 i > 0 时,判断移除窗口的nums[i - 1]是否为最大值(队头元素),如果是,就把队头元素也搞掉,如果不是,那说明最大值还在窗口里,就不用管;
  4. 向右滑动,nums[ i - 1 ]出来了,但是窗口右侧又进来nums[ j ],此时若队列不为空,则为了保证最大值始终在队头,我们让该队列非严格递减,也就是判断队列队尾的元素与nums[ j ]的大小,若nums[j]更大,那么就把这队尾元素移除,反之把nums[ j ]放在它后面;
  5.  每次滑动结束后,若当前窗口左边界 i >= 0,取出队头元素值,存放到结果数组 res 中;
  6. 当滑动窗口完全结束后,返回结果数组 res;

3.2  代码实现

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0 || nums == null) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int j = 0, i = 1 - k; j < nums.length; i++,j++) {
            if(i > 0 && deque.peekFirst() == nums[i-1]) {
                deque.removeFirst();
            }
            while(!deque.isEmpty() && deque.peekLast() < nums[j]) {
                deque.removeLast();
            }
            deque.addLast(nums[j]);
            if(i >= 0) {
                res[i] = deque.peekFirst();
            }
        }
        return res;
    }
}

3.3  代码优化(减少冗余判断) 

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        //未形成窗口前,假定i为窗口的右边界
        for(int i = 0; i < k; i++) {
            while(!deque.isEmpty() && deque.peekLast() < nums[i]) {
                deque.removeLast();
            }
            deque.addLast(nums[i]);
        }
        //保存第1个窗口的最值
        res[0] = deque.peekFirst();
        //形成窗口后,这里从第2个窗口开始
        for(int i = k; i < nums.length; i++) {
            //nums[i-k]表示刚从窗口左侧移出的元素
            if(deque.peekFirst() == nums[i - k]) {
                deque.removeFirst();
            }
            while(!deque.isEmpty() && deque.peekLast() < nums[i]) {
                deque.removeLast();
            }
            deque.addLast(nums[i]);
            res[i - k + 1] = deque.peekFirst();
        }
        return res;
    }
}

3.3  复杂度分析

  • 时间复杂度为O(N),其中N为nums的长度,线性遍历nums占用O(N),每个元素最多出队入队一次,因此队列最多消耗O(2N)的复杂度,故总体复杂度为O(N);
  • 空间复杂度为O(k),双端队列deque最多存储k个元素,res数组消耗O(k)的额外空间,故总体空间复杂度为O(k);

四、学习地址

作者:Krahets

链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-i-hua-dong-chuang-kou-de-zui-da-1-6/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值