title: 239. 滑动窗口最大值
date: 2019-03-03 16:52:20
tags: [algorithms,LeetCode]
题目描述
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 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 ≤ 输入数组的大小,且输入数组不为空。
进阶(本题解法正式如此)
你能在线性时间复杂度内解决此题吗?
使用一个双端队列存放数组的下标索引,遍历数组的同时进行值的比较按照从大到小的顺序从头到尾排列数对应的下标,一旦要放入的数不能满足这一结构就一直从尾部弹出数,直到它放进去仍能保持这种单调的结构
检查队列中的下标是否过期
何为过期:
数不在窗口内即为过期,即窗口移动的时候从左端走出窗口的位置
想一想
双端队列的首部元素是否就是窗口内的最大值呢?
答案: 没错
解释: 队列中都是未过期的数,即以当前位置为窗口的尾部,过期元素下一个元素为窗口首部的中间的部分数(不满足尾部插入的数要弹出),又因为是按照降序排列的,所以首部元素一定是窗口内最大值。
时间复杂度: O(N)
所有数都值进入一次队列,出一次队列,只遍历一遍数组。
class Solution {
public int[] maxSlidingWindow(int[] arr, int w) {
if (arr == null || w < 1 || arr.length < w) {
return new int[0];
}
LinkedList<Integer> qmax = new LinkedList<Integer>();
int[] res = new int[arr.length - w + 1];
int index = 0;
for (int i = 0; i < arr.length; i++) {
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) {
qmax.pollLast();
}
/*
* 无论如何i都会加入qmax双端队列的尾部
*
* qmax队列最少会有一个位置
* 1.这种情况是由与arr[i]比之前的qmax中的所有数都大
* 2.亦或是qmax第一次添加元素
*/
qmax.addLast(i);
/*
* w = 3,i = 4时
* 位置为 1 的就是过期位置
*/
if (qmax.peekFirst() == i - w) {
qmax.pollFirst();
}
/*
* 当 i > 2 的时候才有窗口可言,所以才有窗口内最大值可言
*/
if (i >= w - 1) {
/*
* 最大值不更新这个窗口就一直是这个最大值所以是peekFirst
*/
res[index++] = arr[qmax.peekFirst()];
}
}
return res;
}
}
坑
虽然题目说
注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
但是
给的测试用中却有这
输入
[]
0
预期值
[]
返回值为[]操作
if (arr == null || w < 1 || arr.length < w) {
return new int[0];
}
如果你写的是return null那就是有一个测试(上面的那个用例)用例过不了呀
对于空数组的理解
Java 中长度为 0 的数组与 null 的区别
现有如下两个变量定义:
int[] zeroArray = new int[0];
int[] nullArray = null;
上面这两种定义有什么区别呢?
zeroArray 是一个长度为 0 的数组,称之为 空数组。
- 空数组也是一个对象,只是包含元素个数为 0。
- nullArray 是一个数组类型的空引用。
假设一个方法返回一个数组,如果它返回 null,则调用方法必须先判断是否返回 null,才能对放回数组进一步处理。而如果返回空数组,则无须 null 引用检查。
鉴于此,返回数组的方法在没有结果时,我们通常返回空数组,而不是 null,这样做对于函数调用者的处理比较方便。