问题描述
给定一个整数数组 nums
和一个整数 k
,k
代表滑动窗口的大小,该窗口从数组的最左侧滑动到最右侧。你只能在滑动窗口内看到 k
个数字,每次窗口向右移动一位。要求返回每个滑动窗口中的最大值。
示例
考虑数组 nums = [1,3,-1,-3,5,3,6,7]
和 k = 3
。根据题意,我们需要输出每个窗口的最大值,即 [3,3,5,5,6,7]
。
解题思路1
直观的解法是对每个窗口使用线性搜索来找到最大值,这种方法的时间复杂度是 O(n*k),其中 n 是数组长度。对于大数组和大窗口尺寸,这将非常低效。为了优化这个过程,我们可以使用双端队列(deque),这是一种允许在两端快速插入和删除的数据结构。
使用双端队列
关键思想:使用双端队列来存储每个窗口的潜在最大值的索引。队列的特点是保持从前到后的元素对应的数组值是递减的。这样,队列的头部总是当前窗口的最大值。
- 初始化:创建一个空的双端队列和一个结果数组。
- 遍历数组:
- 对于每个元素,检查队列头部的索引是否已经超出窗口范围。如果是,从队列前端移除。
- 移除队列中所有小于当前元素的值的索引,因为它们不可能是当前或未来窗口的最大值。
- 将当前元素的索引添加到队列后端。
- 一旦窗口形成(即遍历到的索引大于等于
k-1
),队列的头部就是该窗口的最大值,将其添加到结果数组中。
复杂度分析
- 时间复杂度:O(n),每个元素最多被加入和删除一次。
- 空间复杂度:O(k),双端队列在最坏的情况下存储 k 个元素的索引。
代码实现
#include <vector>
#include <deque>
#include <iostream>using namespace std;vector<int> maxSlidingWindow(vector<int>& nums, int k) {deque<int> dq;vector<int> result;for (int i = 0; i < nums.size(); i++) {if (!dq.empty() && dq.front() < i - k + 1) {dq.pop_front();}while (!dq.empty() && nums[dq.back()] <= nums[i]) {dq.pop_back();}dq.push_back(i);if (i >= k - 1) {result.push_back(nums[dq.front()]);}}return result;
}int main() {vector<int> nums = {1, 3, -1, -3, 5, 3, 6, 7};int k = 3;vector<int> result = maxSlidingWindow(nums, k);for (int num : result) {cout << num << " ";}cout << endl;return 0;
}
解题思路2
分块 + 预处理算法思路
此方法将输入数组划分为长度为 k
的多个块,并为每个块预计算两个关键的数组:
- 从左至右的最大值数组(
left
):left[j]
存储从块的开始到索引j
的最大值。 - 从右至左的最大值数组(
right
):right[j]
存储从块的结束到索引j
的最大值。
通过这两个数组,我们可以快速回答任何跨度为 k
的窗口最大值查询。具体做法是:
- 对于一个从索引
i
到i+k-1
的窗口,可以分为两部分:一部分完全在一个块内,另一部分可能跨越两个块。 - 通过比较
right[i]
和left[i+k-1]
的值,我们可以得到窗口的最大值,因为right[i]
给出从窗口开始到块结束的最大值,而left[i+k-1]
给出从块开始到窗口结束的最大值。
复杂度分析
- 时间复杂度:预处理阶段为 O(n),每次查询的复杂度为 O(1),因此总体上非常高效,尤其适合于大量的窗口查询。
- 空间复杂度:O(n),因为我们存储了额外的两个数组。
代码实现
#include <vector>
#include <iostream>using namespace std;vector<int> maxSlidingWindow(vector<int>& nums, int k) {int n = nums.size();vector<int> max_left(n), max_right(n);max_left[0] = nums[0];max_right[n - 1] = nums[n - 1];for (int i = 1; i < n; i++) {max_left[i] = (i % k == 0) ? nums[i] : max(max_left[i - 1], nums[i]);int j = n - i - 1;max_right[j] = (j % k == 0) ? nums[j] : max(max_right[j + 1], nums[j]);}vector<int> result;for (int i = 0; i <= n - k; i++) {result.push_back(max(max_right[i], max_left[i + k - 1]));}return result;
}int main() {vector<int> nums = {1, 3, -1, -3, 5, 3, 6, 7};int k = 3;vector<int> result = maxSlidingWindow(nums, k);for (int num : result) {cout << num << " ";}cout << endl;return 0;
}