编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 01.用类实现一维数组
  • 02.从数组中删除重复项
  • 03.啤酒与饮料
  • 04.二维数组中查找
  • 05.数组中重复的数字
  • 06.和为s的两个数字
  • 07.数组中只出现一次数字
  • 08.数组中只出现一次的数字
  • 09.买卖股票最佳时机
  • 10.调整数组顺序
  • 11.找出常用的数字
  • 12.旋转数组的最小数字
  • 13.调整数组顺序使奇数位于偶数前面
  • 14.顺时针打印矩阵
  • 15.数组中出现次数超过一半的数字
  • 16.最小的k个数
  • 17.连续子数组的最大和
  • 18.把数组排成最小的数
  • 19.数组中的逆序对
  • 20.在排序数组中出现的次数
  • 21.滑动窗口的最大值

21.滑动窗口的最大值

目录介绍

  • 01.题目要求
  • 02.问题分析
  • 03.实例代码

01.题目要求

  • 问题如下所示:
    • 给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
  • 示例 :
    • 例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小为3,那么一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5}。

02.问题分析

  • 如果采用蛮力法,这个问题似乎不难解决:可以扫描每一个滑动窗口的所有数字并找出其中的最大值。如果滑动窗口的大小为k,需要O(k)时间才能找出滑动窗口里的最大值。对于长度为n的输入数组,这个算法总的时间复杂度是O(nk)。
  • 实际上一个滑动窗口可以看成是一个队列。当窗口滑动时,处于窗口的第一个数字被删除,同时在窗口的末尾添加一个新的数字。这符合队列的先进先出特性。如果能从队列中找出它的最大数,这个问题也就解决了。
  • 但我们并不把滑动窗口的每个数值都存入队列中,而只把有可能成为滑动窗口最大值的数值存入到一个两端开口的队列。接着以输入数字{2,3,4,2,6,2,5,1}为例一步分析。
  • 数组的第一个数字是2,把它存入队列中。第二个数字是3.由于它比前一个数字2大,因此2不可能成为滑动窗口中的最大值。2先从队列里删除,再把3存入到队列中。此时队列中只有一个数字3.针对第三个数字4的步骤类似,最终在队列中只剩下一个数字4.此时滑动窗口中已经有3个数字,而它的最大值4位于队列的头部。
  • 接下来处理第四个数字2。2比队列中的数字4小。当4滑出窗口之后2还是有可能成为滑动窗口的最大值,因此把2存入队列的尾部。现在队列中有两个数字4和2,其中最大值4仍然位于队列的头部。
  • 下一个数字是6.由于它比队列中已有的数字4和2都大,因此这时4和2已经不可能成为滑动窗口中的最大值。先把4和2从队列中删除,再把数字6存入队列。这个时候最大值6仍然位于队列的头部。
  • 第六个数字是2.由于它比队列中已有的数字6小,所以2也存入队列的尾部。此时队列中有两个数字,其中最大值6位于队列的头部。
  • 接下来的数字是5.在队列中已有的两个数字6和2里,2小于5,因此2不可能是一个滑动窗口的最大值,可以把它从队列的尾部删除。删除数字2之后,再把数字5存入队列。此时队列里剩下两个数字6和5,其中位于队列头部的是最大值6.
  • 数组最后一个数字是1,把1存入队列的尾部。注意到位于队列头部的数字6是数组的第5个数字,此时的滑动窗口已经不包括这个数字了,因此应该把数字6从队列删除。
  • 那么怎么知道滑动窗口是否包括一个数字?应该在队列里存入数字在数组里的下标,而不是数值。当一个数字的下标与当前处理的数字的下标之差大于或者等于滑动窗口的大小时,这个数字已经从滑动窗口中滑出,可以从队列中删除了。

03.实例代码

  • 如下所示
public class Test {
    private static List<Integer> maxInWindows(List<Integer> data, int size) {
        List<Integer> windowMax = new LinkedList<>();

        // 条件检查
        if (data == null || size < 1 || data.size() < 1) {
            return windowMax;
        }

        Deque<Integer> idx = new LinkedList<>();

        // 窗口还没有被填满时,找最大值的索引
        for (int i = 0; i < size && i < data.size(); i++) {
            // 如果索引对应的值比之前存储的索引值对应的值大或者相等,就删除之前存储的值
            while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) {
                idx.removeLast();
            }

            //  添加索引
            idx.addLast(i);
        }

        // 窗口已经被填满了
        for (int i = size; i < data.size(); i++) {
            // 第一个窗口的最大值保存
            windowMax.add(data.get(idx.getFirst()));

            // 如果索引对应的值比之前存储的索引值对应的值大或者相等,就删除之前存储的值
            while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) {
                idx.removeLast();
            }

            // 删除已经滑出窗口的数据对应的下标
            if (!idx.isEmpty() && idx.getFirst() <= (i - size)) {
                idx.removeFirst();
            }

            // 可能的最大的下标索引入队
            idx.addLast(i);
        }

        // 最后一个窗口最大值入队
        windowMax.add(data.get(idx.getFirst()));

        return windowMax;

    }
}
贡献者: yangchong211
上一篇
20.在排序数组中出现的次数