这篇文章我们来看一道题目求滑动窗口最大值问题(在leetcode上的地址:滑动窗口最大值)
题目描述
给定一个长度为N的数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。返回滑动窗口最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解决方案
一、使用最大堆来实现
首先定义一个大小为K的最大堆,把窗口里面的数据入堆,这样堆顶的数据就是最大值,当窗口向右移动的时候,我们还需要做的一件事情就是把不在窗口的数据移出堆,把进入窗口的数据放入堆,此时堆也会做相应调整,维持最大值在堆顶。代码如下:
public class SlidingWindow {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length == 0 || k < 1 || k > nums.length) {
return null;
}
if(k == 1) {
return nums;
}
int[] result = new int[nums.length - k + 1];
int j = 0;
//用优先队列构建最大堆
PriorityQueue<Integer> queue = new PriorityQueue<>(k, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
if(o1.compareTo(o2) == 0) {
return 0;
}else if(o1.compareTo(o2) > 0) {
return -1;
}else {
return 1;
}
}
});
for(int i=0;i<nums.length;i++) {
//把不在窗口的数据移除掉
if(i-k+1 > 0) {
queue.remove(nums[i-k]);
}
//把移进窗口的数据加入最大堆,最大值一定会在堆顶
queue.add(nums[i]);
if(i-k+1 < 0) {
continue;
}
result[j++] = queue.peek();
}
return result;
}
}
复杂度分析
- 时间复杂度:O(nlogk)
二、使用双端队列来实现
定义一个大小为K的双端队列,把窗口里的数据放入队列,每次入队的时候进行判断,队列里面小于入队数据时,需要出队,一定保持最大值在队列的最左端,代码实现如下:
public class SlidingWindow {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length == 0 || k < 1 || k > nums.length) {
return null;
}
if(k == 1) {
return nums;
}
int[] result = new int[nums.length - k + 1];
int m = 0;
ArrayDeque<Integer> queue = new ArrayDeque<>(k);
for(int i=0;i<nums.length;i++) {
//把不在窗口的数据移除掉
while(!queue.isEmpty() && queue.peek() < i-k+1) {
queue.remove();
}
//比较大小,把较小的数据移除掉,保证最大值在队列的最左端
while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) {
queue.removeLast();
}
queue.add(i);
if(i-k+1 < 0) {
continue;
}
result[m++] = nums[queue.peek()];
}
return result;
}
}
复杂度分析
- 时间复杂度:O(n)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。