最先想到的办法是每个窗中都求最大值,唯一的技巧是保存上一个最大值的位置pos,如果pos出了窗,则遍历当前窗求最值,否则,只需比较窗右边的值和最大值即可。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 0 || nums == null || k == 0) return new int[]{};
int[] res = new int[nums.length - k + 1];
int max_pos = -1;
int max = Integer.MIN_VALUE;
for (int i = 0, j = k-1; j < nums.length; j++, i++) {
if (i > max_pos) { // 左指针已经越过最大值的位置, 则重新在窗口中找最大值
max = Integer.MIN_VALUE;
for (int n = i; n < =j; n++) {
if (max < nums[n]) {
max = nums[n];
max_pos = n;
}
}
} else {
if (max < nums[j]) {
max = nums[j];
max_pos = j;
}
}
res[i] = max;
}
return res;
}
}
此方法平平无奇,居然也可以beat 98%,我也是醉了。
另一种好的方法是采用一个双队列的数据结构,队列保存元素索引。首先判断队列的尾部是否在当前窗内(尾部存放最先入队的元素),不在的话将其去掉,然后比较头部和当前元素nums[i]的大小,如果头部<nums[i],则将双队列头部去掉,否则,将nums[i]添加到头部。最后,因为队列中存的数值(索引号对应到数组中)是降序排列,并且尾部的数字在前面,所以将队列尾部的元素保存到re[]中。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || k <= 0) {
return new int[0];
}
int n = nums.length;
int[] r = new int[n-k+1];
int ri = 0;
// store index
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
// remove numbers out of range k
if (!q.isEmpty() && q.peekLast() < i - k + 1) {
q.pollLast();
}
// remove smaller numbers in k range as they are useless
while (!q.isEmpty() && nums[q.peekFirst()] < a[i]) {
q.pollFirst();
}
// q contains index... r contains content
q.offerFirst(i);
if (i >= k - 1) {
r[ri++] = nums[q.peekLast()];
}
}
return r;
}
}
不难看出,队列中保存的都是当前窗口中的元素,准确地说,是当前窗口中从最大值处到right处呈降序排列的元素(如[2,3,8,4,5,3]变成[8,5,3]),所以尾部元素为这些元素的最大值,应放入re中。i增加1,首先要判断边界(队列是否在窗内),然后与nums[i]比较(如果头部的元素小于nums[i],说明头部元素肯定不是窗内最大的元素,所以要把它去掉)。队列记录索引,便于判断边界。
有个python版的,感觉写得不错。
class Solution(object):
def maxSlidingWindow(self, nums, k):
ans = []
queue = [] # queue存放大数的位置
for i, v in enumerate(nums): # for i,v 就相当于一趟循环搞定了。
if queue and queue[0] <= i - k: # queue里面是index,i-k is gap! j <= i-k => i-j >= k。说明该弹出去了
queue = queue[1:] # 弹出最老的pos,queue现在只有后面的值index
while queue and nums[queue[-1]] < v: # queue还有值,里面的值都< v, 说明queue里面都不是大值,v最大。
queue.pop() # queue清空 一直到queue空或者当前位置的数大于v才停止循环
queue.append(i) # 循环完毕,现在保存i位置上的大数v,存进quue里面v的位置i
if i >= k - 1: # 如果新加的这个i位置比k大。说明可以加入ans了。取值,加入ans。
ans.append(nums[queue[0]]) # 事实上,此时queue中的第一个数要大于当前v,v是第二大,但是第一个数给了ans之后
return ans # 再回到loop里面,就又被弹出了,因此原来是第二大的v现在变成了最大!看下面解释。
"""
c++版本完全一致,适合背诵!!经典题目啊!
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> dq;
vector<int> ans;
for (int i=0; i<nums.size(); i++) {
if (!dq.empty() && dq.front() == i-k) dq.pop_front();
while (!dq.empty() && nums[dq.back()] < nums[i])
dq.pop_back();
dq.push_back(i);
if (i>=k-1) ans.push_back(nums[dq.front()]);
}
return ans;
}
};
Keep indexes of good candidates in deque d. The indexes in d are from the current window, they're increasing, and their corresponding nums are decreasing. Then the first deque element is the index of the largest window value.
这段话终于看明白了,deque里面的特点是index递增,但是对应值递减,这样满足了k之后,就把第一个给ans。
For each index i:
Pop (from the end) indexes of smaller elements (they'll be useless).
Append the current index.
Pop (from the front) the index i - k, if it's still in the deque (it falls out of the window).
If our window has reached size k, append the current window maximum to the output.
"""
# enumerate(nums) will return index and value
# if queue is true and queue[0] <= i - k => k <= i - queue[0]
"""
>>> a = Solution()
>>> nums, k = [1,3,-1,-3,5,3,6,7], 3
>>> print a.maxSlidingWindow(nums, k)
[3, 3, 5, 5, 6, 7]
"""
import collections
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
res = []
d = collections.deque()
for i in range(len(nums)):
while d and d[-1] < nums[i]:
d.pop()
d.append(nums[i])
if i > k - 1 and d[0] == nums[i - k]:
d.popleft()
if i >= k - 1:
res.append(d[0])
return res
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ans = new int[nums.length - k + 1];
if (nums.length == 0) return new int[0];
int[] leftmax = new int[nums.length];
int[] rightmax = new int[nums.length];
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i ++){
if (i % k == 0) max = Integer.MIN_VALUE;
max = Math.max(max, nums[i]);
leftmax[i] = max;
}
max = Integer.MIN_VALUE;
for (int i = nums.length-1; i >= 0; i--){
max = Math.max(max, nums[i]);
rightmax[i] = max;
if (i % k == 0) max = Integer.MIN_VALUE;
}
for (int i = 0; i < nums.length - k + 1; i++){
ans[i] = Math.max(leftmax[i+k-1], rightmax[i]);
}
return ans;
}
}