《代码随想录》第五章 栈与队列 347. 前 K 个高频元素

《代码随想录》第五章 栈与队列 347. 前 K 个高频元素

努力学习!

题目:力扣链接

  • 给你一个整数数组 nums​ 和一个整数 k​ ,请你返回其中出现频率前 k​ 高的元素。你可以按 任意顺序 返回答案。

一、思想

这道题的核心思想是统计频率 + 动态维护前 k 个高频元素

  1. 统计频率

    • 使用哈希表(unordered_map​)统计每个元素出现的频率。
    • 哈希表的查找、插入、删除操作的平均时间复杂度为 O(1),适合高效统计频率。
  2. 动态维护前 k 个高频元素

    • 使用优先队列(最小堆)来动态维护当前频率最高的 k​ 个元素。

      • 最大堆的堆顶是最大元素,无法直接移除最小频率的元素。
    • 优先队列的特点是队首元素是最小值,因此可以通过不断替换最小频率的元素,确保队列中始终是频率最高的 k​ 个元素。

  3. 提取结果

    • 最后从优先队列中提取出频率最高的 k​ 个元素,按从高到低的顺序存入结果数组。

二、代码

class Solution
{
public:
    /**
     * 内部类mycomparison,重载()运算符,用于比较pair的第二个元素的大小
     * 这是一个函数对象(functor),用于定义priority_queue的比较规则
     * 使用大于号(>)实现最小堆,即频率较小的元素在堆顶
     */
    class mycomparison
    {
    public:
        bool operator()(const pair<int, int> &lhs, const pair<int, int> &rhs) { return lhs.second > rhs.second; }
    };
    
    /**
     * 函数topKFrequent,用于找出数组中出现频率最高的k个元素
     * 时间复杂度:O(n log k),其中n是数组长度
     * 空间复杂度:O(n),用于存储哈希表和优先队列
     * @param nums 输入的整数数组
     * @param k 需要找出的元素个数
     * @return 返回出现频率最高的k个元素的数组
     */
    vector<int> topKFrequent(vector<int> &nums, int k)
    {
        // 创建一个哈希表,存储每个元素出现的频率
        // unordered_map使用哈希表实现,查找时间复杂度为O(1)
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); ++i) {
            map[nums[i]]++;  // 统计每个元素出现的次数
        }

        // 创建一个优先队列,按照元素的频率进行排序
        // 使用最小堆,只保留频率最高的k个元素
        // 优先队列的模板参数:
        // 1. 存储的元素类型:pair<int, int>(元素值,频率)
        // 2. 底层容器:vector<pair<int, int>>
        // 3. 比较函数:mycomparison
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
        
        // 使用迭代器遍历哈希表
        for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); ++it) {
            pri_que.push(*it);  // 将元素插入优先队列
            // 如果优先队列的大小超过k,则弹出频率最低的元素
            if (pri_que.size() > k) {
                pri_que.pop();  // 移除堆顶元素(当前最小频率)
            }
        }

        // 创建一个数组,存储出现频率最高的k个元素
        vector<int> res(k);
        // 从后往前填充结果数组,因为优先队列是最小堆,堆顶是最小频率
        for (int i = k - 1; i >= 0; --i) {
            res[i] = pri_que.top().first;  // 获取当前最大频率元素
            pri_que.pop();  // 移除堆顶元素
        }
        return res;
    }
};

三、代码解析

1. 算法工作原理解析及代码分析

1.1 统计频率

unordered_map<int, int> map;
for (int i = 0; i < nums.size(); ++i) {
    map[nums[i]]++;  // 统计每个元素出现的次数
}
  • 目的:统计数组中每个元素出现的频率。

  • 实现

    • 使用 unordered_map​,键为数组元素的值,值为元素出现的次数。
    • 遍历数组,对每个元素 nums[i]​,在哈希表中对应的值加 1。

1.2 构建优先队列(最小堆)

priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
  • 目的:动态维护当前频率最高的 k​ 个元素。

  • 实现

    • 使用 priority_queue​,存储元素类型为 pair<int, int>​(元素值,频率)。
    • 自定义比较函数 mycomparison​,使用 >​ 实现最小堆。

    时间复杂度:每次插入和删除操作的时间复杂度为 O(log k)。

1.3 动态维护前 k 个高频元素

for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); ++it) {
    pri_que.push(*it);  // 将元素插入优先队列
    if (pri_que.size() > k) {
        pri_que.pop();  // 移除堆顶元素(当前最小频率)
    }
}
  • 目的:确保优先队列中始终是频率最高的 k​ 个元素。

  • 实现

    • 遍历哈希表,将每个键值对插入优先队列。
    • 如果队列大小超过 k​,移除堆顶元素(当前最小频率)。
  • 时间复杂度:O(n log k),其中 n​ 是数组长度,k​ 是目标元素个数。

2. 关键点说明

2.1 哈希表的统计频率

  • 关键点:哈希表的高效查找和插入操作。

  • 细节

    • 使用 unordered_map​,平均时间复杂度为 O(1)。
    • 通过 map[nums[i]]++​ 简洁高效地统计频率。

2.2 优先队列(最小堆)的设计

  • 关键点:最小堆的动态维护。

  • 细节

    • 优先队列的模板参数:

      • 存储元素类型pair<int, int>​。
      • 底层容器vector<pair<int, int>>​。
      • 比较函数mycomparison​,使用 >​ 实现最小堆。
    • 最小堆的特点是堆顶元素是最小值,确保队列中始终是频率最高的 k​ 个元素。

2.3 动态维护前 k 个高频元素

  • 关键点:优先队列的大小控制。

  • 细节

    • 每次插入新元素后,检查队列大小是否超过 k​。
    • 如果超过 k​,移除堆顶元素(当前最小频率),确保队列中始终是频率最高的 k​ 个元素。

2.4 提取结果的顺序

  • 关键点:按频率从高到低的顺序提取元素。

  • 细节

    • 优先队列是最小堆,堆顶是最小频率元素。
    • 从后往前填充结果数组,确保结果按频率从高到低排列。

四、复杂度分析

  • 时间复杂度

    • 统计频率:O(n),其中 n​ 是数组长度。
    • 优先队列操作:每次插入和删除的时间复杂度是 O(log k),总共有 n​ 次操作,因此总时间复杂度为 O(n log k)。
    • 总体时间复杂度:O(n log k)。
  • 空间复杂度

    • 哈希表:O(n),存储所有元素的频率。
    • 优先队列:O(k),存储前 k​ 个高频元素。
    • 总体空间复杂度:O(n)。

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

需求响应动态冰蓄冷系统需求响应策略的优化研究(Matlab代码实现)内容概要:本文围绕“需求响应动态冰蓄冷系统需求响应策略的优化研究”展开,基于Matlab代码实现,重点探讨了冰蓄冷系统在电力需求响应背景下的动态建模优化调度策略。研究结合实际电力负荷电价信号,构建系统能耗模型,利用优化算法对冰蓄冷系统的运行策略进行求解,旨在降低用电成本、平衡电网负荷,并提升能源利用效率。文中还提及该研究为博士论文复现,涉及系统建模、优化算法应用仿真验证等关键技术环节,配套提供了完整的Matlab代码资源。; 适合人群:具备一定电力系统、能源管理或优化算法基础,从事科研或工程应用的研究生、高校教师及企业研发人员,尤其适合开展需求响应、综合能源系统优化等相关课题研究的人员。; 使用场景及目标:①复现博士论文中的冰蓄冷系统需求响应优化模型;②学习Matlab在能源系统建模优化中的具体实现方法;③掌握需求响应策略的设计思路仿真验证流程,服务于科研项目、论文写作或实际工程方案设计。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注系统建模逻辑优化算法的实现细节,按文档目录顺序系统学习,并尝试调整参数进行仿真对比,以深入理解不同需求响应策略的效果差异。
综合能源系统零碳优化调度研究(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、付费专栏及课程。

余额充值