数据结构与算法随笔之优先队列-求滑动窗口最大值(三)
这篇文章我们来看一道题目求滑动窗口最大值问题(在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)