目录
- 题目概述
- 解题思路
- 代码实现
- 此题的几个重要知识点
题目概述
此题对应力扣的239.滑动窗口的最大值 题目:
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例:
解题思路
这个题刚开始做的时候,目的很明确使用链表模拟队列,然后随着窗口的移动进行相应的进队和出队,然后再利用Collections的方法max()求最大值。不过放在力扣上一运行超时了。
代码如下:
原因是时间复杂度为O(n2),因为这个Collections的max()方法的时间复杂度就已经是O(n)了。后来我想怎么去简单的求解一个区域里的最大值呢,试了很多方法都不怎么行。
其实这个题的核心思想就是:不保证滑动窗口里的每一个值,但要保证滑动窗口里的可能最大值。
那么怎么去实现呢?我们的理想状态就是说,让这个队列呈现单调的趋势(也就是单调队列),最大值在出口处,滑动窗口每移动一次就把出口处的最大值给弹出去,然后再用一个结果集给收集起来,就大功告成了。想要到达这个理想状态,我们只需要将添加方法,和弹出方法自定义一下即可。
添加方法:每向队列尾端添加元素的时候,都与队尾的元素进行比较,如果队尾的元素比要添加进来的元素小,就把队尾的元素给弹出来,如此一直直到队尾的元素大于等于要添加进来的元素,再将元素从尾部添加即可(前提是队列不为空)。如此即可保持队列的单调型。
弹出方法:当滑动窗口进行移动的时候,要弹出元素,每次要进行弹出的时候都要判断,如果要弹出的这个元素与队列的头部元素(也就是此时队列的最大值)相同,就把队列的首部元素弹出;如果不等于,就不用管。(为什么这么做?这就体现了只对最大值的保护性,也就是如果弹出的跟你这个当前最大值没关系,那就不动他。如果随着滑动窗口的移动弹出来的就是这个最大值,那就要把他弹出来,此时的最大值自然要发生变化。)
窗口每移动一次就将队列头的最大值放入结果集即可。
代码实现
此题的几个重要知识点
逻辑表达式
在写添加方法的时候,程序一直给我报java.util.NoSuchElementException,这就让我非常疑惑,琢磨了半天,最后发现问题就出在一个不容易发现的点上:
代码一:
代码二:
这两个代码的区别就在我把&&前后两个布尔表达式换了一下位置,这就造成了代码一是可以运行的,而代码二是不能运行的。
这其中涉及到了两个知识点:
- && 与 & 的区别
- 双端队列中getLast()方法的特点
首先&&相对于&有个特点:短路,也就是说&&左边的布尔表达式的值一旦等于false,那么右边的是什么东西压根不会看,更不会去执行。如果基础不够扎实,心中总会存在这两个差不多无所谓用哪个的错误想法,因为平时编程序的时候也没报错,但如果一旦涉及这种错误根本找不到。
双端队列中的getLast()方法是会抛异常的,在队列为空的时候。
如此就可以知道为什么代码二会报错了。因为如果getLast()方法一旦报错,这个程序是一定不能继续执行的。而代码一能够执行,是因为他优先判断了队列是否为空,如果不为空它才会执行后面的,而如果为空,他压根就不会再执行后面的代码,getLast()也就不存在报错的可能性。
这种错误以后写代码的时候时一定要注意的。
将Integer列表转化为int数组
如果用for循环一个个去转换再放入一个新的数组会让代码看起来冗余,这里可以用流的转换。
通用格式:
三元表达式的复习
用的少所以爱忘:
基本结构:
变量 = 布尔表达式 ? 如果布尔表达式为true的值:如果布尔表达式为false的值
高级用法:三元表达式也可以嵌套,例如两个值处均可以用三元表达式进行嵌套。