题目:有一个整形数组nums和一个大小为k的窗口,窗口从数组的最左边每次滑动一格直到最右边,返回每次窗口中的最大值。
题目来源:LeetCode--239. 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. Return the max sliding window.
Example:
Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7] Explanation:
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
Note:
You may assume k is always valid, 1 ≤ k ≤ input array's size for non-empty array.
分析:这道题目最直观的解法应该是O(NK)的解法了,依次遍历所有的滑动窗口,找出每个滑动窗口里面的最大值,然后返回,这种解法我们就不写代码了。
第二种思路就是,在找每个窗口内的最大值时,不使用遍历的方式了,而是维护一个容量为k的大顶堆,这样堆顶元素就是滑动窗口的最大值了,这样时间复杂度为O(N*lgK),AC代码如下:
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || k < 1 || nums.length < k) {
return new int[0];
}
PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
int[] res = new int[nums.length - k + 1];
for(int i = 0; i < k; ++i) {
pq.add(nums[i]);
}
res[0] = pq.peek();
for(int i = k; i < nums.length; ++i) {
pq.remove(nums[i - k]);
pq.add(nums[i]);
res[i - k + 1] = pq.peek();
}
return res;
}
第三种思路是维护一个最大容量为k的双端队列(储存的元素的索引不是具体的元素),其尾部插入规则是:当队尾元素大于等于要插入的元素时,直接在队尾插入该元素的索引,否则依次从队尾删除元素,直到队尾元素大于等于要插入的元素或者队列为空时插入新元素索引(这样在双端队列的首部一直维持的是该滑动窗口最大元素的索引)。但队头元素过期时删除队头元素。这样只需要遍历一次数组就得到了最终的结果了,下面看AC代码:
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || k < 1 || nums.length < k) {
return new int[0];
}
Deque<Integer> deque = new ArrayDeque<>(k);
int[] res = new int[nums.length - k + 1];
int cur = 0;
for (int i = 0; i < nums.length; i++) {
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.add(i);
if (i - deque.getFirst() == k) {
deque.removeFirst();
}
if (i >= k - 1) {
res[cur++] = nums[deque.getFirst()];
}
}
return res;
}
好了,我们下期见!