Leetcode: 239. 滑动窗口最大值
题目描述
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 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
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length
思路
思路1:优先级队列
使用优先级队列存储一个k的最大堆,大于k时每次去除前面的第k个元素
思路2:动态规划
将原数组设置为k个一个单元,然后分别求从左至当前位置的最大值(不超过k单元内)和从右至左,这样可以直接求两个边界最大值即可
代码
代码1
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0){
return nums;
}
PriorityQueue pq = new PriorityQueue(Collections.reverseOrder());
int[] result = new int[nums.length - k + 1];
for(int i = 0; i < nums.length; i++){
if (i >= k) pq.remove(nums[i - k]);
pq.offer(nums[i]);
if (i + 1 >= k) result[i + 1 - k] = pq.peek();
}
return result;
}
}
代码2
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0){
return nums;
}
int n = nums.length;
int[] leftDPs = new int[n];
int[] rightDPs = new int[n];
leftDPs[0] = nums[0];
rightDPs[n - 1] = nums[n - 1];
int[] result = new int[n - k + 1];
for(int i = 1; i < n; i++){
if (i % k != 0){
leftDPs[i] = Math.max(nums[i], leftDPs[i - 1]);
} else {
leftDPs[i] = nums[i];
}
int rightIndex = n - i - 1;
if (rightIndex % k != 0){
rightDPs[rightIndex] = Math.max(nums[rightIndex], rightDPs[rightIndex + 1]);
}else {
rightDPs[rightIndex] = nums[rightIndex];
}
}
for(int i = 0; i < n - k + 1; i++){
result[i] = Math.max(rightDPs[i], leftDPs[i + k - 1]);
}
return result;
}
}
复杂度分析
思路1时间复杂度
$O(nlogk)$
思路1空间复杂度
$O(n)$
思路2时间复杂度
$O(n)$
思路2空间复杂度
$O(n)$