Leetcode算法训练日记 | day12

一、滑动窗口的最大值

1.题目

Leetcode:第 239 题

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

示例 1:

输入: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

示例 2:

输入:nums = [1], k = 1
输出:[1]

2.解题思路

1.一般解法(时间复杂度太高):遍历数组,找到所有的长度为k的窗口,并求出每个窗口的最大值。
2.使用单调队列:使用一个单调队列(从大到小)来维护滑动窗口中的最大值。在每次迭代中,它会弹出队列的第一个元素(如果它已经不在当前的滑动窗口内),然后将新的元素添加到队列中,并始终保持队列中的元素是当前窗口内的最大值。这样,在每次迭代结束时,队列的第一个元素就是当前滑动窗口的最大值,可以直接用于更新结果。

3.实现代码

#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
using namespace std;

//一般解法
class Solution1 {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        // 创建一个空的整数向量result,用于存储滑动窗口中的最大值。
        vector<int> result;
        // 使用for循环从数组的开始遍历到数组末尾减去窗口大小k的位置。
        for (int i = 0; i <= nums.size() - k; i++) {
            // 使用auto关键字和范围for循环结合max_element函数找到从索引i开始,长度为k的子数组中的最大元素。
            auto max = max_element(nums.begin() + i, nums.begin() + i + k);
            // 将找到的最大值添加到result向量的末尾。
            // 注意:max是一个迭代器,所以使用*max来获取迭代器指向的元素的值。
            result.push_back(*max);
        }
        // 函数返回包含每个滑动窗口最大值的向量result。
        return result;
    }
};

//使用单调队列
class Solution2 {
private:
    // 定义一个名为MyQueue的内部类,用于实现一个单调队列(从大到小)
    class MyQueue {
    public:
        // 使用deque(双端队列)来实现队列的基本操作
        deque<int> que;

        // 定义pop函数,用于从队列中弹出元素
        // 当需要弹出的数值等于队列头部元素时,执行弹出操作
        // 弹出之前会检查队列是否为空,以避免访问空队列
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front(); // 如果条件满足,弹出队列头部元素
            }
        }

        // 定义push函数,用于向队列中添加元素
        // 当添加的元素(value)大于队列尾部元素时,先弹出队列尾部元素
        // 这样可以保持队列中的元素始终是从大到小的单调顺序
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back(); // 弹出队列尾部元素,直到新元素可以插入
            }
            que.push_back(value); // 将新元素添加到队列尾部
        }

        // 定义front函数,用于获取队列头部元素的值
        // 由于队列是单调递减的,所以头部元素是当前最大值
        int front() {
            return que.front(); // 返回队列头部元素
        }
    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k){
        MyQueue que; // 创建一个MyQueue对象,用于处理滑动窗口
        vector<int> result; // 创建一个vector,用于存储结果

        // 循环处理数组的前k个元素,将它们添加到队列中
        for (int i = 0; i < k; i++) {
            que.push(nums[i]); // 将前k个元素依次添加到队列中
        }
        // 将队列中的最大值(队列头部元素)添加到结果vector中
        result.push_back(que.front());

        // 从数组的第k个元素开始,进行滑动操作
        for (int i = k; i < nums.size(); i++) {
            // 弹出队列中的最旧元素(已经不在当前滑动窗口中的元素)
            que.pop(nums[i - k]);
            // 将数组中的新元素添加到队列中
            que.push(nums[i]);
            // 将队列中的最大值(队列头部元素)添加到结果vector中
            result.push_back(que.front());
        }

        // 返回结果vector,包含了每个滑动窗口的最大值
        return result;
    }
};

//测试
int main()
{
    Solution1 s1;
    Solution1 s2;
    int k = 3;
    vector<int>nums = { 1, 3, -1, -3, 5, 3, 6, 7 };
    cout << "nums = ";
    for (auto& i : nums)
    {
        cout << i << " ";
    }
    cout << endl;
    vector<int>result1=s1.maxSlidingWindow(nums, k);
    cout << "一般解法 result = ";
    for (auto & i : result1)
    {
        cout << i << " ";
    }
    cout <<endl;
    vector<int>result2 = s2.maxSlidingWindow(nums, k);
    cout << "使用单调队列 result = ";
    for (auto& i : result2)
    {
        cout << i << " ";
    }
    cout << endl;
    cout << endl;
    return 0;
}

ps:以上皆是本人在探索算法世界旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。 

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值