LC 239.滑动窗口最大值

239.滑动窗口最大值

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

返回 滑动窗口中的最大值

示例 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]

提示:

  • 1 ≤ n u m s . l e n g t h ≤ 1 0 5 1 \leq nums.length \leq 10^5 1≤nums.length≤105
  • − 1 0 4 ≤ n u m s [ i ] ≤ 1 0 4 -10^4 \leq nums[i] \leq 10^4 −104≤nums[i]≤104
  • 1 ≤ k ≤ n u m s . l e n g t h 1 \leq k \leq nums.length 1≤k≤nums.length

解法一(优先队列)

思路分析:

  1. 对于求滑动窗口的最大值,可以使用优先队列,其中的大根堆可以帮助我们实时维护一系列元素中的最大值
  2. 首先将nums数组的前k个元素放入优先队列中,当向右移动窗口时,将新元素放入优先队列中,此时堆顶的元素是堆中所有元素的最大值,但是这个元素可能不在滑动窗口中
  3. 当最大值不在滑动窗口中时,需要判断该值在数组nums的位置在滑动窗口左边界的左侧,所以在继续移动窗口时,若最大值不在滑动窗口中,则永久移除优先队列
  4. 不断移除堆顶的元素,直到堆顶元素在滑动窗口中,此时堆顶元素为滑动窗口中的最大值
  5. 为了方便判断堆顶元素是否在滑动窗口中,可以使用优先队列存储二元组(num, index)index为元素num在数组中的下标

实现代码如下:

java 复制代码
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
		int n = nums.length;	// 数组nums长度
		// 创建 优先队列 并设置堆中元素比较方式
		PriorityQueue<int[]> pq = new PriorityQueue<>(
				(o1, o2) -> o1[0] != o2[0]? o2[0]-o1[0] : o2[1] - o1[1]
		);
		// 先将第一组滑动窗口 添加到优先队列中
		for (int i = 0; i < k; ++ i) {
			pq.offer(new int[]{nums[i], i});
		}
		int[] ans = new int[n-k+1];		// 返回最大值结果数组 且一共有n-k+1个窗口
        ans[0] = pq.peek()[0];
		// 移动滑动窗口 进行遍历
		for (int i = k; i < n; ++ i) {
			pq.offer(new int[]{nums[i], i});	// 将右窗口元素加入到优先队列中
			while (pq.peek()[1] <= i-k) {	// 将不在窗口内的最大值移除
				pq.poll();
			}
			ans[i-k+1] = pq.peek()[0];	// 记录窗口内的最大值
		}
		return ans;
    }
}

提交结果:

解答成功:

执行耗时:90 ms,击败了11.01% 的Java用户

内存消耗:56.2 MB,击败了96.20% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n l o g n ) O(nlog_{}{n}) O(nlogn),维护优先队列中堆顶最大值时间复杂度为 O ( l o g n ) O(log_{}{n}) O(logn),同时需要遍历每个窗口的时间复杂度为 O ( n − k + 1 ) O(n-k+1) O(n−k+1),所以综合得时间复杂度为 O ( n l o g n ) O(nlog_{}{n}) O(nlogn)
  • 空间复杂度: O ( n ) O(n) O(n),维护优先队列,保存数组中的元素

解法二(单调队列)

思路分析:

  1. 根据解法一进行优化,可以使用一个队列来维护没有被移除的数组元素的下标,并且这些下标对应的元素是严格单调递减的
  2. 当滑动窗口向右移动时,需要将一个新的元素放入队列中,此时需要新元素与队尾的元素比较,即如果队尾元素小于新元素,则永久移除,保证新的元素小于队尾的元素
  3. 且此时队首元素就是滑动窗口中的最大值,但是与解法一中一样,需要判断最大值是否在滑动窗口中,若不在则弹出,并继续进行判断
  4. 因为需要对队首和队尾的元素进行操作,所以使用双端队列来实现单调队列

实现代码如下:

java 复制代码
class Solution {
	public int[] maxSlidingWindow(int[] nums, int k) {
		int n = nums.length;	// 数组nums长度
		Deque<Integer> deque = new ArrayDeque<>();	// 双端队列 用作维护单调队列
		int[] ans = new int[n-k+1];		// 返回结果数组 且计算得滑动窗口数量为n-k+1
		for (int i = 0; i < k; ++i) {	// 将第一组滑动窗口元素加入队列中
			// 将小于新元素的队尾元素 移除队列
			while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
				deque.pollLast();
			}
			deque.offerLast(i);	// 将新元素添加到队尾
		}
		ans[0] = nums[deque.peekFirst()];
		for (int i = k; i < n; ++i) {
			// 将队尾小于新元素的数组元素 移除
			while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()])
				deque.pollLast();
			deque.offerLast(i);		// 将新元素添加到队尾
			// 将队首 不在滑动窗口内的最大值移除
			while (!deque.isEmpty() && deque.peekFirst() <= i-k)
				deque.pollFirst();
			ans[i-k+1] = nums[deque.peekFirst()];
		}
		return ans;
	}
}

提交结果如下:

解答成功:

执行耗时:30 ms,击败了60.89% 的Java用户

内存消耗:63.2 MB,击败了5.02% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),对每个元素遍历一次,且每个元素进队和出队一次
  • 空间复杂度: O ( n ) O(n) O(n),使用双端队列维护单调队列
相关推荐
对许3 分钟前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j
无尽的大道7 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒11 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~14 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
小鑫记得努力16 分钟前
Java类和对象(下篇)
java
binishuaio20 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE22 分钟前
【Java SE】StringBuffer
java·开发语言
老友@22 分钟前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose
wrx繁星点点37 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
王哈哈^_^39 分钟前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt