当前位置: 首页 > news >正文

子串:滑动窗口最大值

题目描述:给定一个整数数组和一个固定长度为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       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7

求解思路1:单调队列

设滑动窗口索引为[j.......i],窗口内的最大值为x

则滑动窗口向右移动一位时,窗口索引变为[j+1......i+1],窗口减去一个数nums[j],加入一个数nums[i+1],要算[j+1........i+1]窗口的最大值y,会出现两种情况:

  1. x还在区间内,那么y=max(nums[i+1],x)
  2. x是nums[j],被移出去了。那么y就得和[j+1......i+1]区间的数重新比较了

根据以上,我们可以维护一个单调非严格递减队列(双端队列且满足队列的数是递增或递减的)

滑动窗口每加入一个新的数,只要新数比已经在窗口的数都大,那么可以把比新数小的数永久删除。队列的第一个元素就是我们的解。

1、形成窗口和未形成窗口分开写

class Solution {public int[] maxSlidingWindow(int[] nums, int k) {// 方法一:单调队列+形成队列和未形成队列分开写int len = nums.length;// 异常判断if (len == 0 || k == 0 || k > len) {return new int[0];}int[] res = new int[len - k + 1];Deque<Integer> queue = new ArrayDeque<>();// 未形成窗口时:窗口区间是[0.....k-1]for (int i = 0; i < k; i++) {while (!queue.isEmpty() && queue.getLast() < nums[i]) {queue.removeLast();}queue.addLast(nums[i]);}res[0] = queue.getFirst(); // 记录第一个窗口的最大值// 形成窗口时:窗口区间是从[1.....k]开始,转成用i表示为[i-k+1.....i]for (int i = k; i < len; i++) {// nums[i-k]可能会在窗口里,移除nums[i-k]if (nums[i - k] == queue.getFirst()) {queue.removeFirst();}while (!queue.isEmpty() && queue.getLast() < nums[i]) {queue.removeLast();}queue.addLast(nums[i]);res[i - k + 1] = queue.getFirst();}return res;}
}

2、形成窗口和未形成窗口合并写

class Solution {public int[] maxSlidingWindow(int[] nums, int k) {// 方法二:单调队列+形成队列和未形成队列合并写int len = nums.length;// 异常判断if (len == 0 || k == 0 || k > len) {return new int[0];}int[] res = new int[len - k + 1];Deque<Integer> queue = new ArrayDeque<>();//窗口[j...i],[i-k+1,0]for (int i = 0, j = 1- k; i < len; j++, i++) {// nums[j-1]可能会在窗口里,移除nums[j-1]if (j > 0 && nums[j-1] == queue.getFirst()) {queue.removeFirst();}while (!queue.isEmpty() && queue.getLast() < nums[i]) {queue.removeLast();}queue.addLast(nums[i]);if (j >= 0) {res[j] = queue.getFirst();}}return res;}
}

求解思路2:大根堆

使用大根堆,nums[j...i]向前滑动nums[j+1,i+1],窗口每加入元素nums[i]时,确保当前堆中没有nums[j+1],然后大根堆的堆顶元素就是我们求的解。

class Solution {public int[] maxSlidingWindow(int[] nums, int k) {int n = nums.length;// 1. 优先队列存放的是二元组(num,index) : 大顶堆(元素大小不同按元素大小排列,元素大小相同按下标进行排列)// num :   是为了比较元素大小// index : 是为了判断窗口的大小是否超出范围PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>(){public int compare(int[] pair1,int[] pair2){return pair1[0] != pair2[0] ? pair2[0] - pair1[0]:pair2[1] - pair1[1];}});// 2. 优选队列初始化 : k个元素的堆for(int i = 0;i < k;i++){pq.offer(new int[]{nums[i],i});}// 3. 处理堆逻辑int[] res = new int[n - k + 1];         // 初始化结果数组长度 :一共有 n - k + 1个窗口res[0] = pq.peek()[0];                  // 初始化res[0] : 拿出目前堆顶的元素for(int i = k;i < n;i++){               // 向右移动滑动窗口pq.offer(new int[]{nums[i],i});     // 加入大顶堆中while(pq.peek()[1] <= i - k){       // 将下标不在滑动窗口中的元素都干掉pq.poll();                      // 维护:堆的大小就是滑动窗口的大小}   res[i - k + 1] = pq.peek()[0];      // 此时堆顶元素就是滑动窗口的最大值}return res;}
}

知识点回顾

  • Queue的实现类有:LinkedList(普通队列)、PriorityQueue(默认小根堆)、ArrayDeque(非线程安全的双端队列)、BlockingQueue(线程安全的阻塞队列)
  • Deque的实现类有:LinkedList(普通队列)、ArrayDeque(非线程安全的双端队列)

(1)如何实现队列:

  • 使用Queue接口的实现类。比如LinkedList、ArrayDeque、PriorityQueue
  • 使用Deque接口的实现类。比如LinkedList、ArrayDeque

(2)如何实现栈:

  • 使用Deque接口的实现类。比如LinkedList、ArrayDeque
  • 直接使用Stack类(线程安全性能低不推荐)。

Deque:
     * 具备完整的双端队列功能,适合栈、队列或双端队列的混合场景。
Queue:
     * 仅支持标准队列操作,若底层用 ArrayDeque 实现,无法直接使用双端方法,需通过强制类型转换调用(不推荐)。


练习地址:239. 滑动窗口最大值 - 力扣(LeetCode)

http://www.dtcms.com/a/361099.html

相关文章:

  • Macbook Air M4 笔记本 ChatTTS 初体验
  • 总线矩阵的原理
  • 番外篇 | YOLO-FireAD:通过注意力逆残差模块与双池化模块融合实现高精度火灾检测
  • GitHub CLI (gh) 全面指南:终端中的 GitHub 工作流革命
  • 前端页面性能优化
  • JavaScript 性能优化实战技术
  • 99、23种设计模式之组合模式(8/23)
  • Map + 函数式接口的策略模式
  • 控制系统仿真之PID校正-利用PID控制器、PID调节器实现(九)
  • Coze源码分析-工作空间-项目开发-后端源码
  • Python爬虫实战:研究 Lines, bars and markers 模块,构建电商平台数据采集和分析系统
  • 【软件开发工程师の校招秘籍】
  • nginx-realip问题解决方案
  • AI 智能体架构中的协议设计三部曲:MCP → A2A → AG-UI
  • 基于单片机宠物项圈/宠物防丢失设计
  • VMware pro16(许可证)+centos 7超详细安装教程
  • Go语言入门学习笔记
  • 如何将照片从电脑传输到安卓设备
  • GitHub 宕机自救指南:应急解决方案与替代平台
  • LeetCode 165. 比较版本号 - 优雅Java解决方案
  • 【JavaScript】async/await 与 Fetch 传参,PUT,PATCH,文件上传,批量删除等前端案例
  • 《WINDOWS 环境下32位汇编语言程序设计》第10章 内存管理和文件操作(1)
  • 在Lumerical FDTD中,磁偶极子通常用于激发TE模式,而电偶极子用于激发TM模式(文心一言)
  • PyCharm中Debug在状态栏显示运行到光标处(run to cursor)
  • 【MySQL基础】MySQL核心操作全解析
  • 会员店谢幕,补贴战上膛:盒马新十年演绎阿里即时零售战略
  • shell脚本函数介绍
  • (9.1)Python测试之记录
  • 面试 TOP101 动态规划专题题解汇总Java版(BM62 —— BM82)
  • 【数学建模学习笔记】数据标准化