题目描述:

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回每个滑动窗口中的最大值数组,时间复杂度要求O(n)

示数例 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]
示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:

输入:nums = [9,11], k = 2
输出:[11]
示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

解题思路:

为什么要用单调队列?

对于每个滑动窗口,我们可以使用 O(k)的时间遍历其中的每一个元素,找出其中的最大值。对于长度为 n 的数组nums 而言,窗口的数量为 n-k+1,因此该算法的时间复杂度为O((n−k+1)k)=O(nk),会超出时间限制,因此我们需要进行一些优化。

详细的思路是什么?

1.想将我们第一个窗口的k个值存入单调双端队列中,单调队列里面的值为单调递减的。如果发现队尾元素小于等于要加入的元素,则将队尾元素出队,直到队尾元素大于新元素时,再让新元素入队,目的就是维护一个单调递减的队列。

2.我们将第一个窗口的所有值,按照单调队列的规则入队之后,因为队列为单调递减,所以队头元素必为当前窗口的最大值,则将队头元素添加到数组中。

3.然后把剩余的nums.length()-k个数按照规则进行入队,维护单调递减队列。

4.每次将队头元素存到返回数组里。

5.返回数组

有什么坑?

算法过程的视频介绍见 https://leetcode-cn.com/problems/sliding-window-maximum/solution/zhe-hui-yi-miao-dong-bu-liao-liao-de-hua-7fy5/

该介绍里面有个思路不清晰(不需要多用一个if来剔除不在当前窗口的数据,改为大于等于队尾数据时直接剔除)

// 对应咱们的红色情况,则是窗口的前一个元素等于队头元素(注意对窗口的前一个元素的理解:这个元素不在窗口内)
if (nums[j - k] == deque.peekFirst()) {
				deque.removeFirst();
}
while (!deque.isEmpty() && deque.peekLast() < nums[j]) {
				deque.removeLast();
}

改为

while (!deque.isEmpty() && deque.peekLast() <= nums[j]) {
				deque.removeLast();
}

完整的代码如下

package com.lzhsite.leetcode.algoritom.practise.slideWindows;

import java.util.Deque;
import java.util.LinkedList;
 

public class LeetCode239滑动窗口最大值 {

	public int[] maxSlidingWindow(int[] nums, int k) {
		int len = nums.length;
		if (len == 0) {
			return nums;
		}
		int[] arr = new int[len - k + 1];
		int arr_index = 0;
		// 我们需要维护一个单调递增的双向队列
		Deque<Integer> deque = new LinkedList<>();
		// 先将第一个窗口的值按照规则入队
		for (int i = 0; i < k; i++) {
			while (!deque.isEmpty() && deque.peekLast() < nums[i]) {
				deque.removeLast();
			}
			deque.offerLast(nums[i]);
		}
		// 存到数组里,队头元素
		arr[arr_index++] = deque.peekFirst();
		// 移动窗口
		for (int j = k; j < len; j++) {

			while (!deque.isEmpty() && deque.peekLast() <= nums[j]) {
				deque.removeLast();
			}
			deque.offerLast(nums[j]);
			arr[arr_index++] = deque.peekFirst();
		}
		return arr;
	}

	public static void main(String[] args) {

		LeetCode239滑动窗口最大值 a = new LeetCode239滑动窗口最大值();
		int[] arr = {1,3,2,3,2,1};
		arr = a.maxSlidingWindow(arr, 3);
		for (int i=0; i < arr.length; i++) {

			System.out.print(arr[i]+" ");
		}

	}

}