题目描述
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入: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
示例 2:
输入:nums = [1], k = 1
输出:[1]
提示:
- 1 <= nums.length <= 105
- -104 <= nums[i] <= 104
- 1 <= k <= nums.length
思路
该题如果用暴力法实现较为简单,只需要在每个窗口的位置都查找最大值即可,但这样时间复杂度就会变高。因为查找最大值的时间复杂度为,那么可不可以只用的时间复杂度进行查找呢?
这时就要用到单调队列。试想下,如果我们将数压入单调队列,那么最大值只会在队列的头部,我们只需要的时间就可以找到最大值。
那么该如何实现单调队列呢?首先就是如果从队列尾压入一个数,我们要判断从尾向头进行判断压入的这个数和队列中的元素的大小关系,将小于该数的直接出队列。其次获取最大值,最大值经过分析,一般都会在队列的头部,所以只需要获取队列的第一个元素即可。最后是出队列,这里只需要判断队列头部元素是否等于要出队列的元素即可,因为在压入队列时,我们已经判断了大小关系,所以该元素可能已经出队列了。
//实现单调队列
class MonotonicQueue {
LinkedList<Integer> q = new LinkedList<>();
public void push(int n) {
//将小于 n 的元素全部删除
while (!q.isEmpty() && q.getLast() < n) {
q.pollLast();
}
//然后将 n 加入尾部
q.addLast(n);
}
public int max() {
return q.getFirst();
}
public void pop(int n) {
if (q.getFirst() == n) {
q.pollFirst();
}
}
}
所以整体的代码如下:
class Solution {
//实现单调队列
class MonotonicQueue {
LinkedList<Integer> q = new LinkedList<>();
public void push(int n) {
//将小于 n 的元素全部删除
while (!q.isEmpty() && q.getLast() < n) {
q.pollLast();
}
//然后将 n 加入尾部
q.addLast(n);
}
public int max() {
return q.getFirst();
}
public void pop(int n) {
if (q.getFirst() == n) {
q.pollFirst();
}
}
}
public int[] maxSlidingWindow(int[] nums, int k) {
MonotonicQueue window = new MonotonicQueue();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (i < k - 1) //先填充 k-1 个
window.push(nums[i]);
else {
//窗口向前滑动,添加下一个数字
window.push(nums[i]);
//记录当前窗口最大值
list.add(window.max());
//移除旧数字
window.pop(nums[i - k + 1]);
}
}
//将list转为int[]
int[] res = new int[list.size()];
for (int i = 0; i < list.size(); i++)
res[i] = list.get(i);
return res;
}
}