力扣Q239滑动窗口最大值SlidingWindowMaximum
Q239SlidingWindowMaximum
简介
给你一个整数数组 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]
https://leetcode.cn/problems/sliding-window-maximum
解题
优先队列
考虑到题目要求是取一段内容里的最大值,可以考虑优先队列,即按一定顺序排列的队列。将区间内数字放到队列中,队头即区间最大值,在滑动窗口时不断移除不在区间内的元素,添加新的元素即可。但这种方法会超时,问题就出现在移除队列的时候,最坏的时间复杂度有 O(N2logn),要考虑更低时间复杂度的方法。
主要优化删除时候的耗时,像前面提到的删除方法是每走一步便要删除一次,而且是遍历删除,要想到当队首元素(最大的元素)不需要被删除时,其余元素也可不删除,因为总有元素比它大,只有当队首元素移动到区间左侧的时候,才需要删除,此时也可从队首开始删除,不需要遍历删除,不断移除队首元素直到队首元素确切的在区间中即可。
由于需要判断队首元素是否在区间外,需要在队列的元素中添加元素位置,构成<元素, 元素位置> 这样一个二元组放到优先队列里面。
时间复杂度为 O(nlogn)
代码如下
public int[] maxSlidingWindow(int[] nums, int k) {
PriorityQueue<int[]> queue = new PriorityQueue<>(((o1, o2) -> o2[0] - o1[0])); //队列按降序排序
int len = nums.length;
int i = 0;
while (i < k){ //队列初始化
queue.add(new int[]{nums[i], i});
i++;
}
int[] rtn = new int[len - k + 1];
rtn[0] = queue.peek()[0];
while (i < len){
queue.add(new int[]{nums[i], i}); //保证队列不空
while (queue.peek()[1] <= i - k) { //当队首元素不在区间范围内是,不断删除队首元素直到符合标准
queue.poll();
}
rtn[i-k+1]=queue.peek()[0];
i++;
}
return rtn;
}
提交结果
单调队列
单调队列是对上面方法的优化,使用双端队列,对队头和队尾操作,时间比优先队列(大小根堆)要快一点。
- 队列中依然是从大到小排列
- 每移动一步,将新值与队尾元素比较,不断出队到队列空或新值小于队尾元素。此时就可出队是因为新值在队列原有元素右侧,一定会晚于元素元素出队,故原有元素可以删除。
- 移除不在区间的元素,可将元素与队首元素比较,如果相同则移除队首元素,不同可不做处理。因为如果要移除的元素是剩余区间最大的且比队首元素小,那么在队首元素插入的时候要移除的元素已经被移除了,不会再队列中存在
代码
public class Q239SlidingWindowMaximum {
static class MyQueue{ //封装一个新类,也可写入主代码里面
Deque<Integer> queue = new LinkedList<>();
void push(int val){ // 插入操作
while(!queue.isEmpty() && val > queue.getLast()){
queue.removeLast();
}
queue.addLast(val);
}
void poll(int val){ //移除操作
if (!queue.isEmpty() && val == queue.getFirst()){
queue.removeFirst();
}
}
int peek(){ //取队首
return queue.getFirst();
}
}
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1 || k == 1){
return nums;
}
MyQueue queue = new MyQueue();
int len = nums.length - k + 1;
int[] rtn = new int[len];
int num = 0;
for (int i = 0; i < k; i++) {
queue.push(nums[i]);
}
rtn[num++] = queue.peek();
for (int i = k; i < nums.length; i++) {
queue.poll(nums[i - k]);
queue.push(nums[i]);
rtn[num++] = queue.peek();
}
return rtn;
}
}
提交结果
可看到时间较前一方法有所缩短