文章目录
题目描述
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
题解思路
方法一:暴力解法
复杂度分析:
- 时间复杂度 O ( n u m s . s i z e ( ) ∗ k ) O(nums.size() * k) O(nums.size()∗k)
- 空间复杂度 O ( n ) O(n) O(n)
代码实现:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size() < k || k <= 0 || nums.empty()) return {};
vector<int> res;
int start = 0, end = k - 1;
while(end < nums.size()){
int temp = nums[start];
for(int i = start+1; i <= end; ++i){
temp = max(temp, nums[i]);
}
res.push_back(temp);
++start;
++end;
}
return res;
}
};
方法二:使用双端队列 deque
- 动态维护一个双端队列,让队列的头部 front() 始终是当前滑动窗口的最大值的索引。
- 这样每次只需要取该索引对应的值,就是当前滑动窗口的最大值了。
- 注意:双端队列中储存的是元素的索引,而不是元素的值。因为当前滑动窗口已经离开队列的头部索引所指向的值时,我们需要弹出当前队列的头部索引。而只有让队列储存的是索引才能办到这一点。
- 之所以用双端队列,是因为容器的头部和尾部都会弹出元素,所以需要一个两端开口的容器。
代码实现:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size() < k || k <= 0 || nums.empty()) return {};
vector<int> res;
deque<int> findMax;
for (int i = 0; i < nums.size(); ++i) {
// i 每加一次,代表滑动窗口向右移一个单位。
// i 指向的是每个滑动窗口的尾部元素(从 i = k - 1 开始)。
if (i >= k && !findMax.empty()) {
// i >= k 是为了确保 findMax.front() 至少为第一个完整的滑动窗口的最大值索引。
// 即至少形成了一个完整的滑动窗口。
res.push_back(nums[findMax.front()]);
}
while (!findMax.empty() && nums[i] >= nums[findMax.back()]) {
// 如果新进来的 nums[i] 大于等于滑动窗口的尾部元素,
// 说明该尾部元素肯定不会是任何滑动窗口的最大元素。
// 就想象公司新来了一个既比你年轻又比你能干(大于等于你)的人,那你就只能被淘汰了,
// 而且是循环淘汰掉所有不如新员工的老员工。
findMax.pop_back();
}
if (!findMax.empty() && i - findMax.front() >= k) {
// 虽然 findMax 的头部是最大的元素的索引,但是如果当前滑动窗口已不包括该索引,
// 那么需要弹出该索引。
// 就想象即使是公司的骨干成员,但是过了35岁也要被淘汰。
findMax.pop_front();
}
findMax.push_back(i);
}
res.push_back(nums[findMax.front()]); // 最后一个滑动窗口还没计算就退出了 for 循环,需补上。
return res;
}
};
如有帮助到您,可以多多点赞、评论鼓励哟~~~