LeetCode239 滑动窗口最大值
题目
思路
- 暴力枚举法
由题目可以想到,每次要找窗口内的最大值,所以通过不断移动窗口位置,通过队列可以轻松找到最大值:
public int[] maxSlidingWindow(int[] nums, int k) {
//1.创建一个数组用于保留每次滑动窗口的最大值
int[] a = new int[nums.length - k + 1];
//2.创建一个队列:用于比较一个滑动窗口的最值
Queue<Integer> queue = new LinkedList<>();
//3.遍历数组元素
for (int i = 0; i < (nums.length - k + 1); i++){
for (int j = i; j < i + k; j++){
if (queue.isEmpty()) {//当队列为空,直接将其入队
queue.offer(nums[j]);
}else {//如果队列不为空,将元素进行比较,使得队列永远只保留最大值
if (queue.peek() < nums[j]){
queue.poll();
queue.offer(nums[j]);
}
}
}
a[i] = queue.poll();
}
return a;
}
这段代码的时间复杂度为 O(n^2),其中 n 是数组的长度。通过分析可以发现,内层循环是一个固定大小的循环,每次循环需要比较窗口大小的元素。而外层循环是一个变化的循环,从索引0到索引nums.length - k
进行遍历。在每次外层循环的迭代中,内层循环需要执行 k 次。所以不出意外,会出现超出时间限制的问题,所以只能另辟蹊径。
- 单调队列解决滑动窗口问题
单调队列:一种特殊的队列数据结构,它在队列的基础上增加了一个单调性的要求。单调队列通常用于在一个滑动窗口中找到最大或最小值,或者解决其他类似的问题。它的主要特点是队列中的元素按照一定的顺序排列,可以保持队列中的元素按照递增或递减的顺序排列。
特点:单调队列在数据入队时,会对数据进行处理,使得队列内数据保持递减
当5入队时,会将4,3,2出队,再将5入队,这样不难看出,队头元素则为最大元素,这与题目如出一折,而且每次只要移动依次窗口,便可知道窗口内的最大值,所以时间复杂度只有O(n),这样就不会出现超时的问题。
单调队列的实现
/**
* 创建单调队列
*/
static class MonotonicQueue {
private LinkedList<Integer> queue = new LinkedList<>();
public int peek(){
return queue.peekFirst();
}
public int pop(){
return queue.pollFirst();
}
public void offer(Integer value) {
//当队列不为空,并且队列最后的元素小于添加的元素
while (!queue.isEmpty() && queue.peekLast() < value){
queue.pollLast();//将小于要添加的元素删除
}
//添加元素
queue.offerLast(value);
}
Java代码实现滑动窗口求最大值
public int[] maxSlidingWindow1(int[] nums, int k) {
//模拟单调队列
LinkedList<Integer> deque = new LinkedList<>();
//用于存储最大值
int[] result = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
//不为空,进行大小判断入队
while (!deque.isEmpty() && deque.peekLast() < nums[i]){
deque.pollLast();//将小于要添加的元素删除
}
deque.offerLast(nums[i]);//入队操作
//2.当遍历k个元素之后,进行保存
if (i >= k - 1){
result[i - k + 1] = deque.peekFirst();
//当窗口内的首元素超过窗口长度时要剔除
if (deque.peekFirst() == nums[i - k + 1]){
deque.pollFirst();
}
}
}
return result;
}