题目
给你一个整数数组 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 Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
int[] result = new int[nums.length - k + 1];
int index = 0;
Deque<Integer> sortDeque = new LinkedList<Integer>();
for (int i = 0; i < k; i++) {
if (sortDeque.isEmpty()) {
sortDeque.offerLast(nums[i]);
} else if (nums[i] <= sortDeque.peekFirst()) {
// 小于队头元素,从队尾开始放入
while (nums[i] > sortDeque.peekLast())
sortDeque.pollLast();
sortDeque.offerLast(nums[i]);
} else {
while (!sortDeque.isEmpty())
sortDeque.pollFirst();
sortDeque.offerFirst(nums[i]);
}
}
result[index++] = sortDeque.peekFirst();
int left = 0;
for (left = 0; left < nums.length - k; left++) {
int right = left + k;
if (nums[left] == sortDeque.peekFirst()) {
sortDeque.pollFirst();
}
if (nums[right] <= sortDeque.peekFirst()) {
// 小于队头元素,从队尾开始放入
while (nums[right] > sortDeque.peekLast())
sortDeque.pollLast();
sortDeque.offerLast(nums[right]);
} else {
while (!sortDeque.isEmpty())
sortDeque.pollFirst();
sortDeque.offerFirst(nums[right]);
}
result[index++] = sortDeque.peekFirst();
}
return result;
}
}
优化
发现如果新加入的元素比队头元素(队列最大值)大的话,同样可以从队尾删除元素直至队列为空。
这样就节省了判断操作,而且代码更加简洁。
2. 用两个指针代替 for循环,增强可读性
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 只关注最大值,用单调队列
// 加入新的以后,旧的且比新元素小的可以直接出对
if (k == 1) return nums;
int[] result = new int[nums.length - k + 1];
int index = 0;
// 1. 单调队列
Deque<Integer> sortQueue = new ArrayDeque<>();
// 2. 将前 k 个元素入队
for (int i = 0; i < k; i++) {
while (!sortQueue.isEmpty() && nums[i] > sortQueue.peekLast()) {
sortQueue.pollLast();
}
sortQueue.offerLast(nums[i]);
}
// 3. 移动窗口 left是要出窗口的,right是要进窗口的
int left = 0, right = k;
while (right < nums.length) {
// 移动窗口前记录当前最大值
result[index++] = sortQueue.peekFirst();
// left 出窗口
if (sortQueue.peekFirst() == nums[left])
sortQueue.pollFirst();
// right 入窗口
while (!sortQueue.isEmpty() && nums[right] > sortQueue.peekLast())
sortQueue.pollLast();
sortQueue.offerLast(nums[right]);
left++;
right++;
}
result[index++] = sortQueue.peekFirst();
return result;
}
}