栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

剑指 Offer 59 - I. 滑动窗口的最大值 (Java解题 单调队列)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

剑指 Offer 59 - I. 滑动窗口的最大值 (Java解题 单调队列)

LeetCode - 剑指 Offer 59 - I. 滑动窗口的最大值
  • 题目描述
  • 题解分析
  • 解题代码
  • 总结


题目描述

难度:困难

给定一个数组 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

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
题解分析

此题可以通过维护一个单调队列这样一种策略来解题。(不熟悉的话可以先学习一下单调队列)

单调队列,简单来说就是按顺序往队列存放一个区域的值时,队列的头部元素是一个区域(窗口) 的最值,无论最大最小都可;然后以此题来说队列会维护成一个降序排列。(如果要求最小值那么就是一个升序排列)

每次窗口移动的时候,队列会去除掉一些元素并添加一个元素,这类变化就是来维护这个队列的特性,即队列的头部是该窗口的最大值,队列降序排序。

窗口移动时,我们都要向队列中添加移动后窗口的最后一个元素并尝试排除原窗口中的第一个元素。

单调队列的特性如何维持呢?

当我们向队列尾部添加一个元素 i 时,为了使队列维持一个降序排序,可以将队列中所有小于该元素 i 的元素剔除,这样这个队列就能维持成降序排序。如队列中元素为 3 -1 -2 时,如果向尾部插入一个元素为 2 ,那么队列元素就会变成 3 2,小于2的元素都会被剔除。

另外,由于是滑动窗口,有一种可能是队列中的头部最大值为上一窗口的值,我们可以在每次滑动时进行判断,如果窗口滑动了,并且队列中的头部最大值为上一窗口的值,那么就将它剔除。

再按照上面维护降序排序的逻辑,往队列尾部放入滑动窗口中的新元素,此时的队列头部就一定是当前窗口的最大值,记录下来即可。

解题代码
    public int[] maxSlidingWindow(int[] nums, int k) {
        if( nums == null || nums.length == 0 || k == 0)  return new int[0];  // 检查

        int[] res = new int[nums.length - k + 1];   // 返回的数组的长度为 多出来的数组长度 + 选中的窗口中的最大值(窗口中会选出1最大值)
        Deque mq = new linkedList<>();  // 单调队列

        // 先处理第一个窗口
        for (int i = 0 ; i < k ; i++){
            while (!mq.isEmpty() && mq.peekLast() < nums[i]) mq.pollLast(); // 剔除队列前面所有比当前插入元素小的值
            mq.offerLast(nums[i]);
        }
        res[0] = mq.peekFirst();   // 此时头部元素是最大值

        for(int i = k ; i < nums.length ; i++){
            if(nums[i - k] == mq.peekFirst()) mq.pollFirst();   // 对队头元素做排查

            while (!mq.isEmpty() && mq.peekLast() < nums[i]) mq.pollLast(); // 剔除队列前面所有比当前插入元素小的值
            mq.offerLast(nums[i]);

            res[i - k + 1] = mq.peekFirst();
        }

        return res;
    }

总结

新学了一手单调队列,感觉用法很神奇,对于求这种运动区域的最值有奇效,再接再厉!



岁月悠悠,衰微只及肌肤;热忱抛却,颓废必致灵魂

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/340520.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号