题目
给定一个数组 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 暴力法
遍历窗口,从0 到 nums.length - k,每个窗口遍历选出最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int [] a = new int[nums.length - k + 1];
for (int i = 0; i <= nums.length - k; i++) {
int max = nums[i];
for (int j = i; j < i + k; j++) {
max = Math.max(max,nums[j]);
}
a[i] = max;
}
return a;
}
}
复杂度分析
时间复杂度O(N*k),循环遍历窗口,每个窗口遍历k次 找最大值
空间复杂度O(N - K + 1),大小为N-K+1的数组存放最大值
暴力法优化
因为窗口每一次移动一格,加入的元素可以判断是否比最大值大,还需要考虑出去的元素是否为最大值,这样不用 重复计算最大值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int [] a = new int[nums.length - k + 1];
int count = 1;
int max = findMax(Arrays.copyOfRange(nums, 0, k));
a[0] = max;
for (int i = 0; i < nums.length - k; i++) {
if (nums[i] < a[i] && nums[i + k] < a[i]) {//下一个窗口,加入的元素比最大值小,并且最大值没有出去
a[i + 1] = a[i];
continue;
}
if (nums[i + k] > a[i]) { //加入的元素比最大值大
a[i + 1] = nums[i + k];
count = 1;
continue;
}
if (nums[i + k] == a[i]) { //加入的元素等于最大值,记录这个元素的个数
a[i + 1] = nums[i + k];
count++;
continue;
}
if (nums[i] == a[i]) { //出去的元素是最大值
count--;
if(count == 0) { //没有最大值了,重新找最大值
a[i + 1] = findMax(Arrays.copyOfRange(nums, i + 1, i + k + 1));
count = 1;
} else { //最大值元素里面还有
a[i + 1] = nums[i];
}
}
}
return a;
}
public int findMax(int[] array) {
int out = Integer.MIN_VALUE;
for (int num : array) {
out = Math.max(num, out);
}
return out;
}
}
复杂度分析
时间复杂度,最坏情况,数组是按照从大到小排序,每一次出去的都是最大值,这样每一次都要找最大值。O(N*K)
空间复杂度O(K)+ O(N-K+1) ,放最大值数组,N-K+1长度,找最大值的数组 长度K
思路2
增加维护一个双端队列,队列里面是单调排序的
这样每次查找最大值时间复杂度只需要O(1),在队列中插入删除元素的时间复杂度也是O(1)。
- 创建一个队列,队列大小长度只为K,在队列中添加删除元素
-首先,头部元素出列,清理空间 - 判断加入的元素,从队列尾一个个删除队列里面比它小的元素
- 在队列尾 加入新的元素
- 返回队列头部,是队列中最大元素
代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> dq = new LinkedList<>(); //队列 存放对应元素的下标
if(n == 0) return nums;
int[] a = new int[n - k + 1];
for(int i = 0; i < n; i++) { //遍历每一个加入元素
//判断队列空间是否大于等于K了,清理空间,头部元素出列
if (!dq.isEmpty() && dq.getFirst() < i - k + 1) {
dq.removeFirst();
}
// 从队列尾删除队列中比加入元素小的元素
while (!dq.isEmpty() && nums[i] >= nums[dq.getLast()]) {
dq.removeLast();
}
dq.addLast(i); //新元素在队列尾加入
if(i >= k - 1) { //当窗口中元素达到K时,开始计算最大值
a[i - k + 1] = nums[dq.getFirst()];
}
}
return a;
}
}
复杂度分析
时间复杂度O(N),只需要遍历一遍数组,在队列中获取最大值为O(1),插入删除操作也为O(1)
空间复杂度O(N),输出数组使用了 O(N - k + 1) 空间,双向队列使用了 O(k)。