题目
给定一个数组 和滑动窗口的大小
,请找出所有滑动窗口里的最大值。
示例:
输入: 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
要求:空间复杂度 ,时间复杂度
。
解题思路:优先队列
对于本题而言,初始时,我们将数组 的前
个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,因此需要判断是否在窗口中,并将不在窗口中的从优先队列中移除。
如何判断最大值是否在滑动窗口中呢?
为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (,表示元素
在数组中的下标为
。对于从左向右遍历下标
就是滑动窗口的右边界,左边界为
,滑动窗口有效区间下标为
。
复杂度分析
时间复杂度: ,其中
是数组
的长度。
- 在最坏情况下,数组  中的元素单调递增,那么最终优先队列中包含了所有元素,没有元素被移除。由于将一个元素放入优先队列的时间复杂度为 因此总时间复杂度为 。
空间复杂度: ,即为优先队列需要使用的空间,在最坏情况下,数组
中的元素单调递增,那么最终优先队列中包含了所有元素。
我的代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
// 按照nums[i]降序,nums[i]相同按照i降序
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
for (int i = 0; i < k; ++i) {
pq.add(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.add(new int[]{nums[i], i});
// 去除无效的最大值,有效左边界i-k+1
while (pq.peek()[1] < i - k + 1) {
pq.poll();
}
// 获取有效最大值
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
}
解题思路:单调队列
算法流程:
初始化: 双端队列 ,结果列表
,数组长度
;
滑动窗口: 左边界范围 ∈,右边界范围 ∈
;
- 依次遍历数组中的元素
当队列非空时,若当前元素大于队尾元素则队尾元素出队 (用以保持 deque 队首至队尾的递减性)
**while(!deque.isEmpty() && num[i] >= deque.getLast()) {deque.pollLast();}**
步骤2 执行完毕,则要么队列为空,要么当前元素小于队尾元素,此时将当前元素添加至队列尾端
**deque.addLast(num[i]);**
检查过期值是否为窗口最大值,排除不在窗口内的最大值
- 将窗口内的最大值加入答案
复杂度分析
时间复杂度: ,该方法需要遍历一次数组所有元素 。
空间复杂度: ,该方法使用常数空间 。
我的代码
public class Solution {
public ArrayList<Integer> maxInWindows(int[] num, int size) {
// 0. 合法性检测
if (num == null || num.length == 0 || size == 0 || num.length < size)
return new ArrayList<Integer>();
// 1. 定义双端队列
LinkedList<Integer> deque = new LinkedList<>();
// 2. 保存结果的数组
ArrayList<Integer> ans = new ArrayList<>();
for (int i = 0; i < num.length; i++) {
// 队列非空,若当前元素大于队尾元素则队尾元素出队
while (!deque.isEmpty() && deque.getLast() <= num[i]) {
deque.pollLast();
}
// 队尾添加当前元素(此时队空或者当前元素小于队尾元素)
deque.addLast(num[i]);
// 检查过期值是否为窗口最大值
if (i - size >= 0 && num[i - size] == deque.getFirst())
deque.pollFirst();
// 将窗口最大值加入答案
if (i >= size - 1) ans.add(deque.getFirst());
}
return ans;
}
}