滑动窗口的最大值(剑指Offer59)

滑动窗口的最大值(剑指Offer59)

1 题目描述

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

示例:

输入: 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 暴力法解题思路

方法一:暴力法,两层循环,第一层循环遍历数组,指示每一层窗口的起始位置,第二层循环遍历窗口找出最大值。

窗口数量:count=nums.length-k+1;即为求解数组大小。

最后一个窗口的地址:nums.length-k,即第一层循环结束的条件。

算法流程:

  1. 标记第一个窗口左端位置start(初始为0)
  2. startnums.length-k位置找到最大值
  3. 将最大值存入解数组x[start]=max
  4. start++,重复1-3;

3 代码

public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length<1||k<1){
            return new int[0];
        }
        int start = 0;
        int count = nums.length-k+1;
        int[] x = new int[count];
        while(start<=nums.length-k){
            int max = nums[start]; 
            for(int j = start;j<start+k;j++){
                //找出最大的
                if (max<nums[j]){
                   max=nums[j];
                }
            }
            x[start]=max;
            start++;
        }
        return x;
    }

4 复杂度分析

第一层循环遍历数组直到nums.length-k+1第二层循环遍历窗口k;因此时间复杂度为:
( n u m s . l e n g t h − k + 1 ) ∗ k = ( n − k + 1 ) ∗ k = n k − k 2 + k = O ( n k ) (nums.length-k+1)*k=(n-k+1)*k=nk-k^2+k=O(nk) (nums.lengthk+1)k=(nk+1)k=nkk2+k=O(nk)

5 优化思路

寻找窗口最大值需要的时间为O(k),而第一层遍历数组是无法避免的,因此本题重点要将寻找窗口内最大值的时间复杂度降低为O(1)。

可采用双端队列,窗口没移动一次保证队列中元素非严格递减,最大的元素在队首,当队首元素与将被移出的元素nums[i-1]相等时,队首元素出队。并将队内所有小于将要被包含的元素nums[i]出队并将该元素从队尾入队,保证队内元素递减

算法流程:

  1. 形成第一个窗口,并形成队列保证队列元素单调递减,将队首元素加入结果集
  2. 形成窗口后,当队首元素与将被移出的元素nums[i-1]相等时,队首元素出队
  3. 将队内所有小于将要被包含的元素nums[i]出队并将该元素从队尾入队,保证队内元素递减
  4. 将队首元素加入结果集

代码:

 public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length<1||k<1){
            return new int[0];
        }
        int count = nums.length-k+1;
        int last = nums.length-k;
        LinkedList<Integer> qu = new LinkedList<>();
        int[] res = new int[count];
        //形成窗口
        for(int i = 0;i<=k-1;i++){
            while(qu.size()!=0&&qu.getLast()<nums[i]){
                qu.removeLast();
            }
            qu.addLast(nums[i]);
        }
        res[0]=qu.getFirst();
        for(int i = 1,j=k;i<=last;i++,j++){
            if(nums[i-1]==qu.getFirst()){
                qu.removeFirst();
            }
            while(qu.size()!=0&&qu.getLast()<nums[j]){
                qu.removeLast();
            }
            qu.addLast(nums[j]);
            res[i]=qu.getFirst();
        }
        return res;
    }

复杂度分析:
时间复杂度 O(n) : 其中 n 为数组 nums 长度;线性遍历 nums 占用 O(n);每个元素最多仅入队和出队一次,因此单调队列 deque占用O(2n)
空间复杂度 O(k) : 双端队列deque 中最多同时存储k个元素(即窗口大小)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值