《代码随想录》第五章 栈与队列 239. 滑动窗口最大值

《代码随想录》第五章 栈与队列 239. 滑动窗口最大值

努力学习!

题目:力扣链接

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

    返回 滑动窗口中的最大值

一、思想

这道题的核心思想是使用单调队列

单调队列是一种特殊的队列,它能够维护队列中元素的单调性(单调递增或单调递减)。在这道题中,我们需要维护一个单调递减的队列,以确保队列的头部始终是当前窗口的最大值。

为什么使用单调队列?

  • 滑动窗口的最大值问题需要高效地找到每个窗口中的最大值。如果直接遍历每个窗口,时间复杂度会很高(O(n*k))。
  • 单调队列通过维护一个单调递减的队列,能够在 O(1) 时间内获取当前窗口的最大值,同时通过移除不必要的元素,保持队列的简洁性。

二、代码

class Solution
{
private:
    /**
     * 自定义队列类,用于维护滑动窗口中的最大值
     */
    class myQueue
    {
    public:
        deque<int> que; // 使用双端队列存储窗口中的元素

        /**
         * 弹出元素:只有当要弹出的元素等于队首元素时才真正弹出
         * @param value 要弹出的元素值
         */
        void pop(int value)
        {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        };

        /**
         * 压入元素:维护队列的单调递减性
         * @param value 要压入的元素值
         */
        void push(int value)
        {
            // 移除所有小于当前值的元素,保持队列单调递减
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value); // 将当前值加入队列
        }

        /**
         * 获取当前窗口的最大值(即队首元素)
         * @return 当前窗口的最大值
         */
        int front() { return que.front(); }
    };

public:
    /**
     * 滑动窗口最大值算法
     * @param nums 输入数组
     * @param k 滑动窗口的大小
     * @return 所有窗口的最大值
     */
    vector<int> maxSlidingWindow(vector<int> &nums, int k)
    {
        myQueue que;     // 创建自定义队列
        vector<int> res; // 存储结果

        // 初始化第一个窗口
        for (int i = 0; i < k; ++i) {
            que.push(nums[i]); // 将前k个元素加入队列
        }
        res.push_back(que.front()); // 记录第一个窗口的最大值

        // 滑动窗口
        for (int i = k; i < nums.size(); ++i) {
            que.pop(nums[i - k]);       // 移除窗口最左边的元素
            que.push(nums[i]);          // 加入窗口最右边的元素
            res.push_back(que.front()); // 记录当前窗口的最大值
        }

        return res; // 返回所有窗口的最大值
    }
};

三、代码解析

1. 算法工作原理分解

  • 步骤 1:初始化单调队列

    • 创建一个单调递减队列 myQueue​,用于存储当前窗口中的元素。
    • 使用 deque<int>​ 实现队列,因为双端队列支持在头部和尾部的高效操作。
  • 步骤 2:初始化第一个窗口

    • 将前 k​ 个元素加入单调队列,并记录第一个窗口的最大值。
  • 步骤 3:滑动窗口

    • 对于每个新窗口,执行以下操作:

      1. 移除窗口左边界元素:如果窗口左边界元素等于队列头部元素,则移除队列头部元素。
      2. 加入窗口右边界元素:将新元素加入队列,并移除所有比它小的元素,保持队列的单调递减性。
      3. 记录当前窗口的最大值:队列头部元素即为当前窗口的最大值。
  • 步骤 4:返回结果

    • 将所有窗口的最大值存储在结果数组 res​ 中,并返回。

2. 关键点说明

  • 单调队列的维护

    • 单调队列的核心在于保持队列的单调递减性。每次加入新元素时,移除所有比它小的元素,确保队列头部始终是当前窗口的最大值。
  • 窗口滑动的处理

    • 窗口滑动时,需要判断窗口左边界元素是否等于队列头部元素。如果相等,则移除队列头部元素,因为该元素已经不在当前窗口中。
  • 时间复杂度优化

    • 通过单调队列的设计,每个元素最多被加入和移除队列一次,因此总体时间复杂度为 O(n)。
  • 空间复杂度优化

    • 单调队列最多存储 k​ 个元素,因此空间复杂度为 O(k)。

四、复杂度分析

  • 时间复杂度O(n)

    • 每个元素最多被加入和移除队列一次,因此总体时间复杂度为 O(n)。
  • 空间复杂度O(k)

    • 单调队列最多存储 k​ 个元素,因此空间复杂度为 O(k)。

白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》

源码来自:https://pan.quark.cn/s/fdd21a41d74f 正方教务管理系统成绩推送 简介 使用本项目前: 早晨睡醒看一遍教务系统、上厕所看一遍教务系统、刷牙看一遍教务系统、洗脸看一遍教务系统、吃早餐看一遍教务系统、吃午饭看一遍教务系统、睡午觉前看一遍教务系统、午觉醒来看一遍教务系统、出门前看一遍教务系统、吃晚饭看一遍教务系统、洗澡看一遍教务系统、睡觉之前看一遍教务系统 使用本项目后: 成绩更新后自动发通知到微信 以节省您宝贵的时间 测试环境 正方教务管理系统 版本 V8.0、V9.0 如果你的教务系统页面下图所示的页面完全一致或几乎一致,则代表你可以使用本项目。 目前支持的功能 主要功能 每隔 30 分钟自动检测一次成绩是否有更新,若有更新,将通过微信推送及时通知用户。 相较于教务系统增加了哪些功能? 显示成绩提交时间,即成绩何时被录入教务系统。 显示成绩提交人姓名,即成绩由谁录入进教务系统。 成绩信息按时间降序排序,确保最新的成绩始终在最上方,提升用户查阅效率。 计算 计算百分制 对于没有分数仅有级别的成绩,例如”及格、良好、优秀“,可以强制显示数字分数。 显示未公布成绩的课程,即已选课但尚未出成绩的课程。 使用方法 Fork 本仓库 → 开启 工作流读写权限 → → → → → 添加 Secrets → → → → → → Name = Name,Secret = 例子 程序会自动填充 尾部的 ,因此你无需重复添加 对于部分教务系统,可能需要在 中添加 路径,如: 开启 Actions → → → 运行 程序 → → 若你的程序正常运行且未报错,那么在此之后,程序将会每隔 30 分钟自动检测一次成绩是否有更新 若你看不懂上述使用...
综合能源系统零碳优化调度研究(Matlab代码实现)内容概要:本文围绕“综合能源系统零碳优化调度研究”,提供了基于Matlab代码实现的完整解决方案,重点探讨了在高比例可再生能源接入背景下,如何通过优化调度实现零碳排放目标。文中涉及多种先进优化算法(如改进遗传算法、粒子群优化、ADMM等)在综合能源系统中的应用,涵盖风光场景生成、储能配置、需求响应、微电网协同调度等多个关键技术环节,并结合具体案例(如压缩空气储能、光热电站、P2G技术等)进行建模仿真分析,展示了从问题建模、算法设计到结果验证的全流程实现过程。; 适合人群:具备一定电力系统、能源系统或优化理论基础,熟悉Matlab/Simulink编程,从事新能源、智能电网、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①开展综合能源系统低碳/零碳调度的科研建模算法开发;②复现高水平期刊(如SCI/EI)论文中的优化模型仿真结果;③学习如何将智能优化算法(如遗传算法、灰狼优化、ADMM等)应用于实际能源系统调度问题;④掌握Matlab在能源系统仿真优化中的典型应用方法。; 阅读建议:建议结合文中提供的Matlab代码网盘资源,边学习理论模型边动手调试程序,重点关注不同优化算法在调度模型中的实现细节参数设置,同时可扩展应用于自身研究课题中,提升科研效率模型精度。
### 关于队列的数据结构解析 #### 双端队列 (Deque) 双端队列是一种特殊的数据结构,允许在两端高效地执行插入和删除操作。Python 中的 `collections.deque` 提供了这种功能,其设计使得它非常适合处理需要频繁在一端或两端进行元素增删的操作场景[^1]。 以下是 `deque` 的一些常用方法及其作用: - **append(x)**: 将元素 x 添加到右端。 - **appendleft(x)**: 将元素 x 添加到左端。 - **pop()**: 移除并返回右端的元素。 - **popleft()**: 移除并返回左端的元素。 下面是一个简单的代码示例展示如何使用 `deque`: ```python from collections import deque dq = deque() dq.append(1) # 在右侧添加元素 1 dq.appendleft(2) # 在左侧添加元素 2 print(dq.popleft()) # 输出 2 并将其从左侧移除 print(dq.pop()) # 输出 1 并将其从右侧移除 ``` #### 队列 (Queue) 队列是一种遵循 FIFO(First In First Out,先进先出)原则的数据结构。标准库中的 STL 队列并不提供迭代器支持,因此无法像列表那样直接遍历其中的元素[^3]。然而,在实际编程中,我们通常会借助其他工具来实现更灵活的功能。 对于 C++ 而言,虽然 STL 队列不具备可迭代特性,但它可以通过底层容器(如 vector 或 list)间接访问内部存储的内容。而在 Python 中,则可以利用 `queue.Queue` 类或者继续沿用前面提到过的 `deque` 来模拟传统意义上的队列行为。 #### 映射表 (Map/Hash Map) 映射表也是一种重要的数据结构形式之一,主要用于建立键值对之间的关联关系。当面对查找频率较高的需求时尤为适用。例如在一个给定数组里统计各个数值出现次数的任务就可以通过哈希映射轻松解决[^2]。 这里给出一段基于 map 统计整数数组各元素频次的例子: ```cpp #include <iostream> #include <unordered_map> int main(){ int nums[] = {1, 2, 3, 2, 4}; std::unordered_map<int,int> freq; for(auto num : nums){ freq[num]++; } for(const auto& pair : freq){ std::cout << "Number:" << pair.first << ", Frequency:"<<pair.second<<std::endl; } } ``` 以上程序片段展示了如何运用 unordered_map 对一组数字进行快速有效的频率分析。 --- ### 总结 无论是作为基础构建模块还是高级算法的核心组件,理解这些基本概念以及它们之间相互转换的能力都是非常必要的。希望上述解释能够帮助您更好地掌握有关队列以及其他相关联的知识点!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值