-
如题:
-
思路:使用单调队列,即先构建初始窗口,大小为K,将前面K个元素依次加入队列中,加入的时候需要保证队头元素是最大的,由于队列先进先出,这样可以保证
poll()
弹出的元素是最大的。- 这样保证队列中队头元素最大:每次加入的时候和当前队尾元素比较(因此应该使用双端队列,可以操作队头和队尾),如果队列非空就拿队尾和当前元素比较,如果当前元素更大,就删除这个队尾元素,因为它不可能是最大值了。然后直到找到大于当前元素的新队尾,将当前元素插入到这个元素之后。具体细节在代码中注释标明!!
-
解法如下:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// res数组记录每一轮窗口最大值,大小为K的窗口总共有num.lenth-k+1轮,
// 即数组长度为num.lenth-k+1
int[] res = new int[nums.length - k + 1];
LinkedList<Integer> monotonicQueue = new LinkedList<>();
for (int i = 0; i < k; i++) {//将前面k个元素形成窗口,之后就只需拿新加入的元素和窗口内的比较
while (!monotonicQueue.isEmpty() && monotonicQueue.getLast() < nums[i]){
/*如果队列非空就拿队列最后一个和当前元素比较,如果当前元素更大,就删除这个队尾元素,因为它不可能是最大值了。
但是注意:如果相同则不应该删除,因为每一轮 窗口右移 后都应该删除上一最左边的元素,
如果上一轮最左边的不是最大值,那么它就不会在队列里(上述规则说明了,在它后面的元素如果比他大会将它压出队列),
这种情况就不用删除。如果它是最大值,那么它就会在队头位置,所以我们窗口右移删除的时候只需要判断:
窗口最左边的元素 和 队列中的队头元素是否相等,如果相等则说明窗口最左边元素在队列中,
且是最大值,应该删除,否则该元素不在队列中,不用删除。
此时如果新加入的元素和它相等,那么如果你在刚刚比较的时候将<=新元素的都删除的话,
那么这个队头元素就在此时被删除,而之后我们将窗口右移的时候判断最左边元素与队头元素是否相等,
此时结果便是true,我们会将队头元素删除,但是这个队头元素实则是新加入的,造成误删!!!!*/
monotonicQueue.pollLast();
}
monotonicQueue.add(nums[i]);
}
//从第k+1个位置即索引为k的位置和前面k个元素形成的窗口依次比较
for (int i = k; i < nums.length; i++) {
res[i-k] = monotonicQueue.peek();//每一轮比较前将上一轮最大的元素记录
if(nums[i-k] == monotonicQueue.peek()){
/*删除上一轮最左边的元素,因为窗口右移了
关键:这里只和队首元素比较,即最大元素,因为上一轮最左边的元素如果不是最大值,
则代表后面几位有比它更大的,而比它更大的元素在入队前会检查队尾元素是否比自己小,
如果比自己小则将这个元素删除,因为我们要返回的是最大值。
因此如果上一轮最左边的元素不是上一轮窗口的最大值,则肯定已经被移出了队列*/
monotonicQueue.remove();//如果它是最大值则移出队列
}
while (!monotonicQueue.isEmpty() && monotonicQueue.getLast() < nums[i]){
monotonicQueue.pollLast();
}
monotonicQueue.add(nums[i]);
}
res[nums.length - k] = monotonicQueue.peek();
//之前循环到nums.length-1后就结束了,res中的最后一个元素位置是nums.length-k,而不是
// nums.lenth - 1 - k,因此应该补上
return res;
}
}