滑动窗口的最大值(剑指Offer59)
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
2 暴力法解题思路
方法一:暴力法,两层循环,第一层循环遍历数组,指示每一层窗口的起始位置,第二层循环遍历窗口找出最大值。
窗口数量:count=nums.length-k+1
;即为求解数组大小。
最后一个窗口的地址:nums.length-k
,即第一层循环结束的条件。
算法流程:
- 标记第一个窗口左端位置
start
(初始为0) - 从
start
至nums.length-k
位置找到最大值 - 将最大值存入解数组
x[start]=max
start++
,重复1-3;
3 代码
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length<1||k<1){
return new int[0];
}
int start = 0;
int count = nums.length-k+1;
int[] x = new int[count];
while(start<=nums.length-k){
int max = nums[start];
for(int j = start;j<start+k;j++){
//找出最大的
if (max<nums[j]){
max=nums[j];
}
}
x[start]=max;
start++;
}
return x;
}
4 复杂度分析
第一层循环遍历数组直到nums.length-k+1
第二层循环遍历窗口k
;因此时间复杂度为:
(
n
u
m
s
.
l
e
n
g
t
h
−
k
+
1
)
∗
k
=
(
n
−
k
+
1
)
∗
k
=
n
k
−
k
2
+
k
=
O
(
n
k
)
(nums.length-k+1)*k=(n-k+1)*k=nk-k^2+k=O(nk)
(nums.length−k+1)∗k=(n−k+1)∗k=nk−k2+k=O(nk)
5 优化思路
寻找窗口最大值需要的时间为O(k),而第一层遍历数组是无法避免的,因此本题重点要将寻找窗口内最大值的时间复杂度降低为O(1)。
可采用双端队列,窗口没移动一次保证队列中元素非严格递减,最大的元素在队首,当队首元素与将被移出的元素nums[i-1]
相等时,队首元素出队。并将队内所有小于将要被包含的元素nums[i]
出队并将该元素从队尾入队,保证队内元素递减
算法流程:
- 形成第一个窗口,并形成队列保证队列元素单调递减,将队首元素加入结果集
- 形成窗口后,当队首元素与将被移出的元素
nums[i-1]
相等时,队首元素出队 - 将队内所有小于将要被包含的元素
nums[i]
出队并将该元素从队尾入队,保证队内元素递减 - 将队首元素加入结果集
代码:
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length<1||k<1){
return new int[0];
}
int count = nums.length-k+1;
int last = nums.length-k;
LinkedList<Integer> qu = new LinkedList<>();
int[] res = new int[count];
//形成窗口
for(int i = 0;i<=k-1;i++){
while(qu.size()!=0&&qu.getLast()<nums[i]){
qu.removeLast();
}
qu.addLast(nums[i]);
}
res[0]=qu.getFirst();
for(int i = 1,j=k;i<=last;i++,j++){
if(nums[i-1]==qu.getFirst()){
qu.removeFirst();
}
while(qu.size()!=0&&qu.getLast()<nums[j]){
qu.removeLast();
}
qu.addLast(nums[j]);
res[i]=qu.getFirst();
}
return res;
}
复杂度分析:
时间复杂度 O(n) : 其中 n 为数组 nums
长度;线性遍历 nums
占用 O(n)
;每个元素最多仅入队和出队一次,因此单调队列 deque
占用O(2n)
。
空间复杂度 O(k) : 双端队列deque
中最多同时存储k
个元素(即窗口大小)。