LeetCode 239. Sliding Window Maximum(滑动窗口最大值)
Posted jmspan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 239. Sliding Window Maximum(滑动窗口最大值)相关的知识,希望对你有一定的参考价值。
原题网址:https://leetcode.com/problems/sliding-window-maximum/
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
For example,
Given nums = [1,3,-1,-3,5,3,6,7]
, and k = 3.
Window position Max --------------- ----- [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
Therefore, return the max sliding window as [3,3,5,5,6,7]
.
Note:
You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array.
Follow up:
Could you solve it in linear time?
Hint:
- How about using a data structure such as deque (double-ended queue)?
- The queue size need not be the same as the window’s size.
- Remove redundant elements and the queue should store only elements that need to be considered.
既要找最值,又能够直接访问到指定的元素,最直接的方法就是使用SortedMap了。
public class Solution
public int[] maxSlidingWindow(int[] nums, int k)
if (k<=0) return new int[0];
TreeMap<Integer, Integer> map = new TreeMap<>();
for(int i=0; i<k-1; i++)
Integer count = map.get(nums[i]);
if (count == null) count = 1; else count ++;
map.put(nums[i], count);
// System.out.println(map);
int[] max = new int[nums.length-k+1];
for(int i=0,j=k-1; j<nums.length; i++, j++)
Integer count = map.get(nums[j]);
if (count == null) count = 1; else count ++;
map.put(nums[j], count);
max[i] = map.lastKey();
count = map.get(nums[j-k+1]);
count --;
if (count == 0) map.remove(nums[j-k+1]); else map.put(nums[j-k+1], count);
// System.out.println(map);
return max;
方法二:使用最大堆,理论上来说这个时间复杂度为O(n*k),因为需要从堆内删除任意一个元素,这个删除操作的时间复杂度应该是O(k)(如有错误请指正),不知为何实测性能更高一些,可能跟test case有关。
public class Solution
public int[] maxSlidingWindow(int[] nums, int k)
if (k<=0) return new int[0];
PriorityQueue<Integer> queue = new PriorityQueue<>();
for(int i=0; i<k-1; i++)
queue.add(-nums[i]);
// System.out.println(map);
int[] max = new int[nums.length-k+1];
for(int i=0,j=k-1; j<nums.length; i++, j++)
queue.add(-nums[j]);
max[i] = -queue.peek();
queue.remove(-nums[j-k+1]);
return max;
方法三:使用tournament tree。由于固定窗口,这种情况下使用tournament tree比使用sorted map更加轻量,但在算法复杂度上属于同一级别的,都是O(n*logk)。
public class Solution
int[] tournament;
int p;
int k;
private void add(int num)
int pos = k-1+p;
tournament[pos] = num;
while (pos>0)
int pair = (pos & 1) == 0 ? pos - 1 : pos + 1;
int max = tournament[pos] >= tournament[pair] ? tournament[pos] : tournament[pair];
if (max == tournament[(pos-1)/2]) break;
tournament[(pos-1)/2] = max;
pos = (pos-1)/2;
p = (p+1) % k;
public int[] maxSlidingWindow(int[] nums, int k)
if (k<=0) return new int[0];
int[] max = new int[nums.length-k+1];
tournament = new int[k+k-1];
Arrays.fill(tournament, Integer.MIN_VALUE);
p = 0;
this.k = k;
for(int i=0; i<k-1; i++) add(nums[i]);
for(int i=0, j=k-1; j<nums.length; i++, j++)
add(nums[j]);
max[i] = tournament[0];
return max;
方法四:使用双向队列,时间复杂度O(n)。
在队列中维持一个k长度窗口内的递减元素下标,为什么呢?因为当元素递增时,前面的元素就不需要了,因为最大值肯定不会是它们了。
顺序扫描每一个元素,当队头的元素超出窗口视野的时候,将对头元素出队;然后检查队尾,如果队尾元素小于或等于当前元素,则队尾元素出队,重复检查队尾直至队列为空或者队尾元素大于当前元素。然后当前元素入队。
这个方法我没有做出来,网上搜索的,感谢网友分享,其中一篇:https://segmentfault.com/a/1190000003903509
这个方法太精妙了,让我想起一道求histagram中最大矩形面积的题目。
参考网友的代码:
public class Solution
public int[] maxSlidingWindow(int[] nums, int k)
if(nums == null || nums.length == 0) return new int[0];
LinkedList<Integer> deque = new LinkedList<Integer>();
int[] res = new int[nums.length + 1 - k];
for(int i = 0; i < nums.length; i++)
// 每当新数进来时,如果发现队列头部的数的下标,是窗口最左边数的下标,则扔掉
if(!deque.isEmpty() && deque.peekFirst() == i - k) deque.poll();
// 把队列尾部所有比新数小的都扔掉,保证队列是降序的
while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) deque.removeLast();
// 加入新数
deque.offerLast(i);
// 队列头部就是该窗口内第一大的
if((i + 1) >= k) res[i + 1 - k] = nums[deque.peek()];
return res;
使用ArrayDeque实现:
public class Solution
public int[] maxSlidingWindow(int[] nums, int k)
if (k <= 0) return new int[0];
int[] max = new int[nums.length - k + 1];
ArrayDeque<Integer> deque = new ArrayDeque<>();
for(int i = 0; i < nums.length; i++)
while (!deque.isEmpty() && nums[deque.getLast()] <= nums[i])
deque.removeLast();
while (!deque.isEmpty() && i - deque.getFirst() >= k)
deque.removeFirst();
deque.add(i);
if (i >= k - 1)
max[i - k + 1] = nums[deque.getFirst()];
return max;
如果自己实现双向队列,则性能可以大幅提升:
public class Solution
public int[] maxSlidingWindow(int[] nums, int k)
if (k<=0) return new int[0];
int[] dequeue = new int[k];
int[] max = new int[nums.length-k+1];
int pos = 0, len = 0;
int j=0;
for(int i=0; i<nums.length; i++)
if (len > 0 && i-dequeue[pos]>=k)
pos = (pos+1) % k;
len --;
if (len == 0) dequeue[(pos+(len++))%k] = i;
else
while (len > 0 && nums[dequeue[(pos+len-1)%k]] <= nums[i]) len --;
dequeue[(pos+(len++))%k] = i;
if (i>=k-1) max[j++] = nums[dequeue[pos]];
return max;
以上是关于LeetCode 239. Sliding Window Maximum(滑动窗口最大值)的主要内容,如果未能解决你的问题,请参考以下文章
leetcode 239-Sliding Window Maximum(hard)
[leetcode]239. Sliding Window Maximum滑动窗口最大值
leetcode-hard-array-239. Sliding Window Maximum