题目链接:https://leetcode-cn.com/problems/sliding-window-maximum/
题目介绍:
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。
返回滑动窗口最大值。
示例:
注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
进阶:
你能在线性时间复杂度内解决此题吗?
解题思路:
一、暴力得天下
- 通过两个for循环得出最大值
- 外层for循环控制滑动窗口移动多少次
- 内层for循环控制每次滑动窗口中的值
- 每次在内层for循环中求出的最大值放入到数组并最后返回
public class Que239 {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 0 || k > nums.length){
return new int[0];
}
int[] result = new int[nums.length - k + 1];
int index = 0;
for (int i = 0; i < nums.length - k; i++) {
int max = nums[i];
for (int j = i+1; j < k+i; j++) {
if (max < nums[j]){
max= nums[j];
}
}
result[index++] = max;
}
return result;
}
public static void main(String[] args) {
int[] ints = new Que239().maxSlidingWindow(
new int[]{1,3,-1,-3,5,3,6,7}
,3);
System.out.println(ints);
}
}
总结:
计算窗口的最大值需O(k),移动n-k+1次,时间复杂度为O(kn)
二、使用双端队列
- 当每次需要往滑动窗口加入数的时候,判断滑动窗口内的是否小于要加入的,如果小于,就弹出
- 在进行往加入数的时候,将左边的弹出
- 队列的值是从大到小的,每次只需要取最左边的值即可
public class Que239 {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length < 2){
return nums;
}
//保存当前窗口最大值的数组位置
LinkedList<Integer> queue = new LinkedList<Integer>();
int[] result = new int[nums.length-k+1];
for (int i = 0; i < nums.length; i++) {
//保存从大到小,如果后面的比前面大,就依次弹出
while (!queue.isEmpty() && nums[queue.peekLast()] < nums[i]){
queue.pollLast();
}
queue.addLast(i);
//如果当前队首是已移动窗口,则弹出
if (queue.peek() <= i-k){
queue.poll();
}
//当窗口长度为K时,保存结果
if (i+1 >= k){
result[i+1-k] = nums[queue.peek()];
}
}
return result;
}
public static void main(String[] args) {
int[] ints = new Que239().maxSlidingWindow(
new int[]{1,3,-1,-3,5,3,6,7}
,3);
System.out.println(ints);
}
}
总结:
由于每个元素最多进队和出队各一次,因此该算法时间复杂度为O(N)。
代码地址:
https://github.com/Han-YLun/LeetCode/blob/master/Practice/src/Que239.java
文章为阿伦原创,如果文章有错的地方欢迎指正,大家互相交流。