剑指 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

解法一:滑动窗口+双端队列+单调

滑动窗口总体上分成两类,一类是可变长度的滑动窗口,一类是固定长度的滑动窗口,这道题目就是固定长度的。在遍历元素时,为了保持窗口的大小固定,右侧元素进入窗口后,左侧元素要能够出去。然后直到遍历结束。

想一下刚才的过程,右侧元素进入,左侧元素出去,这不就是双端队列吗?所以这道题目可以借助双端队列来解;

想一下我们经常会遇到求一个队列或者一个窗口一个栈内的最大最小值,怎么求呢,最简单的方法就是遍历这个窗口这个栈,这样时间复杂度就是O(N),有没有办法能在O(1)时间内获得一个栈或者一个窗口内的最值呢,这其实就是剑指offer30题,比如获取一个栈内的最小值,我们可以采用一个辅助栈,这个辅助栈有一个最大的特点就是单调的,也就是我们俗称的单调栈。比如我们维持一个单调递减栈,如果当前值比栈顶元素大,那就不要了,因为我们最后只获取最小值,如果比当前栈顶小,那就入栈,也就是更新了最小值;这样就可以在O(1)的时间内获得栈内最小值了,因为最小值就是辅助栈的栈顶。

这道题目也类似啊,我们需要获得窗口内的最大值,这不就是一个双端队列的最大值吗,所以我们要维持一个单调递减的双端队列,如何实现呢,每次入队前,判断此值与队尾元素的大小,小于的话就入队,这样就维持了一个单调递减队列;如果元素比队尾值要大,那就要将队尾元素出队了,因为我们只关注大的值,可不能把这个大值错过了,这里面的小值就不用管了。

比如[5,3,4], 4要入队的时候发现3比其小,所以3从队尾出去,4入队;

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length;
if(len == 0) return nums;
int[] res = new int[len-k+1];
Deque<Integer> deque = new LinkedList<>(); //双端队列;
int index = 0;
//未形成窗口;
for(int i = 0; i < k; i++){
while(!deque.isEmpty() && nums[i] > deque.peekLast()){
deque.removeLast(); //保证单调递减队列;
}
deque.offerLast(nums[i]);
}
res[index++] = deque.peekFirst(); //队首始终是最大的;
//滑动窗口;
for(int i = k; i < len; i++){ //i代表当前窗口最后一个元素的索引;
//保证队内只含有窗口内的元素,所以当窗口的前一个元素等于队首的时候,要将队首出队;
if(deque.peekFirst() == nums[i-k]){
deque.removeFirst();
}
while(!deque.isEmpty() && nums[i] > deque.peekLast()){
deque.removeLast(); //保证单调递减队列;
}
deque.offerLast(nums[i]);
res[index++] = deque.peekFirst(); //队首始终是当前窗口内最大的;
}
return res;
}
}

解法二:滑动窗口+单调

上面的做法我们每次入队的是元素的值,本质上就是用双端队列来模拟了窗口的滑动,双端队列是单调队列;

其实我们也可以用一个单调队列,入队的是元素的下标索引。这样其实我们能很明显的看出窗口的滑动,只要队首元素的下标<窗口的左边界,那就要把队首移除了,窗口进行了一次滑动;一个很明显的不同,入队的是下标索引

流程

  • 遍历给定数组中的元素,如果队列不为空且当前考察元素大于等于队尾元素,则将队尾元素移除。直到,队列为空或当前考察元素小于新的队尾元素;
  • 当队首元素的下标小于滑动窗口左侧边界left时,表示队首元素已经不再滑动窗口内,因此将其从队首移除。
  • 由于数组下标从0开始,因此当窗口右边界right+1大于等于窗口大小k时,意味着窗口形成。此时,队首元素就是该窗口内的最大值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length-k+1];
if(nums.length == 0 || nums == null) return new int[]{}; //特例为空;
Deque<Integer> deque = new LinkedList<>();
//right 为窗口右边界;
for(int right = 0; right < nums.length; right++){
//如果队列不为空且当前考察值>队尾元素,将队尾元素移除,直到为空或遇到大的;
while(!deque.isEmpty() && nums[right] > nums[deque.peekLast()]){
deque.removeLast();
}
deque.offerLast(right); //存储下标;
int left = right-k+1; //窗口左侧边界下标;
if(deque.peekFirst() < left){
deque.removeFirst(); //窗口进行了移动,左侧出去;
}
if(right + 1 >= k){
res[left] = nums[deque.peekFirst()]; //这时候窗口形成,开始逐步得到答案;
}
}
return res;
}
}

体会

  • 滑动窗口一共有两种类型:

    • 窗口长度可变:这种类型中长度是可以变化的,一个基本的流程就是,右边界长,然后到达某一个条件(比如窗口内的和达到某个值,窗口中出现了重复的元素),这时候右边界停下来,左边界长,然后跳出这个条件(比如窗口内的和又小于目标值了,比如窗口中又没有重复元素了),这时候右边界再去移动;(我们要处理的始终保证窗口内满足某个条件,例如窗口内的值小于某值,窗口内没有重复的,只要不满足了就去移左边界);
    • 窗口长度固定,比如说固定一个长度的窗口的时候,那右边界长的时候,左边界也得跟着长,维持一个窗口的恒定值;
  • 要始终明白滑动窗口的左右边界是不会出现回退的,两个边界肯定都是朝着一个方向前进的,不会走回头路。

  • 其次要知道滑动窗口其实就是一个队列,右边界移动就是有新元素入队了,左边界移动就是有元素出队了,所以在做题的时候可以想象成一个队列在进行处理,可能会想的更清楚;

参考链接

滑动窗口的最大值

【剑指offer】59 - I. 滑动窗口的最大值的更多相关文章

  1. 剑指 Offer 59 - I. 滑动窗口的最大值 + 双指针 + 双端队列

    剑指 Offer 59 - I. 滑动窗口的最大值 Offer_59_1 题目详情 方法一:暴力方法+双指针 package com.walegarrett.offer; /** * @Author ...

  2. 力扣 - 剑指 Offer 59 - I. 滑动窗口的最大值

    题目 剑指 Offer 59 - I. 滑动窗口的最大值 思路1(单调队列) 使用单调(递减)队列,保持队列中的元素是递减顺序,队列头保存的是当前窗口中最大的元素 首先先模拟建立第一个窗口,同时获取第 ...

  3. 剑指offer 面试题. 滑动窗口的最大值

    题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6 ...

  4. 剑指offer系列38----滑动窗口的最大值(不懂????????????????????????????????????????????????)

    [题目] 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6 ...

  5. 剑指 Offer 59 - II. 队列的最大值--滑动窗口的建模+Deque的基本使用(常用方法)

    剑指 Offer 59 - II. 队列的最大值 题目链接 package com.walegarrett; /** * @Author WaleGarrett * @Date 2020/12/3 1 ...

  6. 《剑指offer》面试题59 - I. 滑动窗口的最大值

    问题描述 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值. 示例: 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5 ...

  7. 剑指offer-面试题59_1-滑动窗口的最大值-数组

    /* 题目: 链接:https://www.nowcoder.com/questionTerminal/1624bc35a45c42c0bc17d17fa0cba788 来源:牛客网 给定一个数组和滑 ...

  8. 剑指Offer 59. 按之字形顺序打印二叉树 (二叉树)

    题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 题目地址 https://www.nowco ...

  9. [剑指Offer] 59.按之字形顺序打印二叉树

    题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. [思路]先按层次遍历存入,通过设立标志位,将 ...

随机推荐

  1. 5、rsync全网备份

    定时备份rsync(增量备份,无差异备份,daemon进程)+crontab,主要备份一些任务脚本和配置文件,如果此时有用户增加数据, 如果是增量备份的话不会备份下来,因为在备份的那一刻,数据已经被锁 ...

  2. hdu 3397 Sequence operation 线段树 区间更新 区间合并

    题意: 5种操作,所有数字都为0或1 0 a b:将[a,b]置0 1 a b:将[a,b]置1 2 a b:[a,b]中的0和1互换 3 a b:查询[a,b]中的1的数量 4 a b:查询[a,b ...

  3. POJ 1015 Jury Compromise dp

    大致题意: 从n个候选人中选出m个人作为陪审团.为了让陪审团的选择更公平,辩方和控方都为这n个候选人给出了满意度(辩方为D[j],控方为P[j],范围0至20).现在要使得选出的m位候选人的辩方总和与 ...

  4. “限时分享“ 本地80个小游戏 HTML+CSS+JS源码分享

    ​ 里面有80款小游戏源码,支持内置导航,可以拿来练手或者消磨时间,具体功能以及游戏请看下图 ​ ​ ​ ​ ​ ​ ​ ​ 维京战争小游戏源码 链接:https://pan.baidu.com/s/ ...

  5. 使用了gitlab管理pipeline,Jenkinsfile 中在出现克隆命令流水线执行会混乱

    Jenkins_pipeline关闭默认检出 问题描述 在使用 Pipeline(流水线)过程中,如果使用了(Pipeline script from SCM)远程 Git 的 Jenkinsfile ...

  6. AcWing 242. 一个简单的整数问题

    给定长度为N的数列A,然后输入M行操作指令. 第一类指令形如"C l r d",表示把数列中第l~r个数都加d. 第二类指令形如"Q X",表示询问数列中第x个 ...

  7. 学会这些CSS技巧让你写样式更加丝滑

    目录 1,前言 1,calc() 2,min() 3,max() 4,clamp() 5,gap 6,writing-mode 1,前言 记录一些很好用的css属性 1,calc() calc()函数 ...

  8. weblogic项目转为tomcat之后出现的问题

    解决java - JAX-WS和版本冲突 itPublisher分享于2017-03-19 推荐:JWS,JAX-WS,JAX-RS,REST,Restlet,SOAP(JAVA Web Servic ...

  9. CG-CTF 签到

    int __cdecl sub_401340(unsigned __int8 *a1) { int v2; // [esp+18h] [ebp-D0h] int v3; // [esp+1Ch] [e ...

  10. ROS2学习之旅(4)——理解ROS2 Graph中的节点

    ROS(2)图(ROS(2) graph)是一个同时处理数据的基于ROS2元素的网络,它包含了所有的可执行文件以及它们之间的连接.图中的基本元素包括:节点(nodes).话题(topics).服务(s ...