1. 题目
给定一个数组 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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
注意:本题与主站 239 题相同:https://leetcode-cn.com/problems/sliding-window-maximum/
Related Topics 队列 Sliding Window
👍 268 👎 0
2. 题解
2.1 解法1: 优先队列(堆)
- 设置最大堆, 其中保存二元组 (nums[i],i)
- 将前k个元素加入堆, 然后移动右指针, 遍历 k 之后的元素
- 当 堆顶元素 的 index 小于等于 当前左指针值时, 说明当前堆顶元素不在窗口内, 出堆,
否则 当前堆顶元素即为 当前窗口的最大值, 将其加入结果集即可 - 由于过程中使用了 k-1, 注意先排除 k=0的情况
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] ans = new int[n - k + 1];
PriorityQueue<int[]> maxHeap = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] != o2[0] ? o2[0] - o1[0] : o2[1] - o1[1];
}
});
// 将前k 个元素入堆
for (int i = 0; i < k; i++) {
maxHeap.offer(new int[]{nums[i], i});
}
// 将第一个元素堆顶加入结果集
ans[0] = maxHeap.peek()[0];
int t = 1;
// 开始移动窗口, 此时左边界下标为 i-k+1
for (int i = k; i < n; i++) {
maxHeap.offer(new int[]{nums[i], i});
while (maxHeap.peek()[1] < i - k + 1) {
maxHeap.poll();
}
ans[t++] = maxHeap.peek()[0];
}
return ans;
}
}
参考:官方题解
2.2 解法2: 单调递减双端队列
- 维护一个存储下标的元素值单调递减的双端队列, 其中队头为最大值, 队尾为最小值
- 然后移动有边界, 执行循环遍历
(1) 先对队列进行入队前的递减维护, 当队列不空且队尾元素小于等于右边界, 队尾元素出队
(2) 将右边界元素加入队尾
(3) 如果队头元素下标小于左边界, 队头出队
(4) 判断是否形成窗口, 若是将结果加入结果集中
队列的队头相当于保存了当前窗口的最大值, 每次先维护其单调递减性, 然后将元素加入队列, 接着判断队头是否在窗口内并做出队头操作, 保证队头最大且在窗口内, 最后取得队头最大值为当前窗口最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 0 || k < 1) return new int[]{};
Deque<Integer> deque = new LinkedList<>();
int[] ans = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
deque.offerLast(i);
// 判断队首元素是否在左边界内, 不是就出队, 直到在左边界
while (deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
if (i - k + 1 >= 0) {
ans[i - k + 1] = nums[deque.peekFirst()];
}
}
return ans;
}
}