单调队列是一种特殊的队列数据结构,它在处理特定类型的算法问题时非常有用,特别是那些涉及滑动窗口的问题。在单调队列中,元素按照某种单调性(单调递增或单调递减)排列。这意味着队列的元素要么是按照非递增的顺序排列,要么是按照非递减的顺序排列。
单调队列的主要特点和应用如下:
单调性:
应用场景:
操作:
实现:
优势:
综上所述,单调队列是解决滑动窗口及相关问题的强大工具,它通过保持元素的单调性来优化查询效率,特别是在需要频繁查询窗口内的最大或最小值时。
使用单调队列解决滑动窗口问题的主要思路是利用队列的单调性来快速获得窗口内的最大值或最小值。这种方法特别适用于那些要求在一个数组或列表上对每个固定大小的连续子数组(窗口)进行某种计算的问题。以下是使用单调队列解决滑动窗口问题的基本步骤:
定义单调队列:
处理新元素:
维持窗口大小:
查询窗口值:
更新和输出结果:
举个例子,假设你有一个数组 [4, 3, 5, 2, 6, 2, 3]
,并且你需要找到每个大小为3的窗口的最大值。你可以使用一个单调递减队列,每次移动窗口时,确保队列的头部元素是该窗口的最大值。当你从左到右遍历数组时,队列首先填充 [4]
,然后变成 [4, 3]
,接着是 [5]
(因为4和3小于5,被移除),以此类推。每次窗口移动时,队列的头部元素就是当前窗口的最大值。
这种方法的优势在于其时间复杂度。对于每个元素,入队和出队操作平均只需要常数时间(O(1)),因此整个算法的时间复杂度是线性的(O(n)),其中n是数组或列表的长度。
算法实现:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<Integer>();
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()];
for (int i = k; i < n; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
while (deque.peekFirst() <= i - k) {
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
}