力扣第347题 堆(优先队列) 经典题 c++ 简易注释版 附(相关知识点解答)

news/2024/5/20 6:57:40 标签: 数据结构, 算法, c++, leetcode, 优先队列,

题目

347. 前 K 个高频元素

中等

相关标签

数组 哈希表 分治 桶排序 计数 快速选择 排序 优先队列

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

思路和解题方法

        首先,在函数内部定义了一个 unordered_map,用于存储每个元素出现的次数。然后使用 for 循环遍历整个数组,将每个元素出现的次数存储到 map 中。

        接着,定义了一个 mycomparison 类,重载了小于号运算符 operator(),用于在priority_queue中实现从小到大排序(即小顶)。小顶的第二个值即为元素出现次数,按次数从小到大排序,代码如下:

class mycomparison {
public:
    bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) { 
        return lhs.second > rhs.second; // 小于号的比较操作,根据second(即频率)从小到大排序
    }
};

        接着,在函数内部定义了一个小顶 priority_queue,用于存储出现次数最多的前 k 个元素。使用 for 循环遍历 map 中所有的键值对,将当前元素的键值对插入小顶中,并保证小顶的大小不超过 k。当小顶的大小超过 k 时,弹出顶,保证的大小恰好为 k。

        最后,再从小顶中取出前 k 个元素,按照他们在数组中出现的次数从大到小输出到结果向量中,即可得到出现次数最多的前 k 个元素。

复杂度

        时间复杂度:

                O(nlogk)

时间复杂度为 O(nlogk),其中 n 是数组的长度,k 是要求的前 k 个元素的个数。具体分析如下:

  1. 遍历整个数组并统计每个元素出现的次数,需要 O(n) 的时间复杂度。

  2. 创建小顶,并将键值对插入中。插入每个元素的时间复杂度为 O(logk),因为的大小最大为 k,所以插入 k 个元素的时间复杂度为 O(klogk)。

  3. 取出前 k 个元素,并倒序输出到结果向量中,需要 O(k) 的时间复杂度。

时间复杂度为 O(nlogk + klogk)。当 k 相对于 n 较小时,可以简化为 O(nlogk)。

        空间复杂度

                O(n+k)

使用了一个 unordered_map 存储每个元素出现的次数,需要 O(n) 的额外空间。同时,使用了小顶来存储前 k 个元素,其大小为 k,所以额外空间复杂度为 O(k)。总的空间复杂度为 O(n+k)。

c++ 代码

 ​
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 统计元素出现频率,使用哈希表unordered_map
        unordered_map<int, int> countMap;
        for (int num : nums) {
            countMap[num]++;
        }

        // 创建小顶,并维护大小为k,使用优先队列priority_queue
        // 小顶中存储pair<pair<int, int>>,第一个元素表示元素出现的频率,第二个元素表示对应的元素值
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;
        for (auto& p : countMap) {
            // 将当前键值对(p.first为元素值,p.second为元素出现的频率)插入到小顶中
            // 注意:插入时要将pair的第一个元素赋值为出现频率,第二个元素赋值为元素值
            minHeap.emplace(p.second, p.first);
            if (minHeap.size() > k) {
                // 如果小顶的大小超过了k,则弹出顶元素,保证的大小为k
                minHeap.pop();
            }
        }

        // 输出前k个高频元素,从小顶中取出元素
        vector<int> result(k);
        for (int i = k - 1; i >= 0; --i) {
            result[i] = minHeap.top().second; // 取出小顶顶元素(即出现频率最小的元素),并将其值存储到结果数组中
            minHeap.pop(); // 弹出顶元素
        }
        
        return result;
    }
};

class Solution {
public:
    // 定义一个小于号比较类,用于小顶的排序
    class mycomparison {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second; // 按出现次数从少到多排序
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map; // 定义哈希表来统计元素出现频率
        for (int num : nums) { // 遍历数组
            map[num]++; // 更新哈希表中对应元素的出现次数
        }

        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pq; // 定义一个小顶

        for (auto& p : map) { // 遍历哈希表中的键值对
            pq.push(p); // 将键值对插入到小顶中
            if (pq.size() > k) { // 如果小顶的大小超过k,则弹出顶元素
                pq.pop(); // 弹出的是出现次数最少的元素
            }
        }

        vector<int> res(k); // 定义结果数组
        for (int i = k - 1; i >= 0; i--) { // 从小顶顶开始依次取出前K个高频元素
            res[i] = pq.top().first; // 取出元素(顶),存储到结果数组中
            pq.pop(); // 弹出顶元素,即出现次数最少的元素
        }

        return res;
    }
};

相关知识

1.优先队列priority_queue

代码定义了一个优先队列priority_queue,用于存储出现频率最高的k个元素。具体来说,它的模板参数如下所示:

priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;

其中:

  • pair<int, int> 表示队列中的元素类型为pair,第一个元素是int类型的出现频率,第二个元素是int类型的元素值。

  • vector<pair<int, int>> 表示底层容器使用vector存储元素。

  • greater<pair<int, int>> 表示采用greater作为比较函数,即小顶,按照出现频率从小到大进行排序。

因此,我们可以通过将键值对(出现次数,元素值)插入到优先队列中,并保证队列大小不超过k。

2.小于号比较操作符重载函数

  bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second; // 按出现次数从少到多排序
        }
    };

定义了一个小于号比较操作符重载函数。该函数接受两个pair<int, int>类型的参数lhsrhs,分别表示中的两个元素。在函数内部,它比较了这两个元素的第二个成员(即出现次数),并根据比较结果返回一个布尔值。

  • 如果lhs.second(左边元素的出现次数)大于rhs.second(右边元素的出现次数),则返回true,表示左边元素应该排在右边元素之后;
  • 如果lhs.second小于等于rhs.second,则返回false,表示左边元素应该排在右边元素之前或者与之相等。

由于我们希望中的元素按照出现次数从少到多的顺序排列,因此在比较函数中使用了 运算符,使得中的元素按照从小到大的顺序进行排序。

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。


http://www.niftyadmin.cn/n/5065466.html

相关文章

计算机网络——三种交换方式(电路交换、分组交换、报文交换以及优缺点)

目录 电路交换&#xff08;Circuit Switching&#xff09; 分组交换&#xff08;Packet Switching&#xff09; 报文交换&#xff08;Message Switching&#xff09; 对比 电路交换优缺点 报文交换优缺点 分组交换优缺点 电路交换&#xff08;Circuit Switching&#x…

【数据科学】Scikit-learn[Scikit-learn、加载数据、训练集与测试集数据、创建模型、模型拟合、拟合数据与模型、评估模型性能、模型调整]

这里写目录标题 一、Scikit-learn二、加载数据三、训练集与测试集数据四、创建模型4.1 有监督学习评估器4.1.1 线性回归4.1.2 支持向量机(SVM)4.1.3 朴素贝叶斯4.1.4 KNN 4.2 无监督学习评估器4.2.1 主成分分析(PCA)4.2.2 K Means 五、模型拟合5.1 有监督学习5.2 无监督学习 六…

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418)

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418&#xff09; 12.1、漏洞原理 Ruby on Rails是一个使用 Ruby 语言写的开源 Web 应用框架&#xff0c;它是严格按照 MVC 结构开发的。它努力使自身保持简单&#xff0c;来使实际的应用开发时的代码更少&#xff0c;使用最少…

踩坑日记 《正确的使用Vuex》基于 uniapp Vue3 setup 语法糖 vuex4 项目 太多坑了要吐了

踩坑日记 《正确的使用Vuex》基于 uniapp Vue3 setup 语法糖 vuex4 项目 太多坑了要吐了 完美解决页面数据不刷新 或者数据慢一步刷新 页面使用html <template><view><template v-if"cartData.data.length>0"><!-- 自定义导航栏 --><…

基于Redis实现消息队列的实践

为什么要基于Redis实现消费队列&#xff1f; 消息队列是一种典型的发布/订阅模式&#xff0c;是专门为异步化应用和分布式系统设计的&#xff0c;具有高性能、稳定性及可伸缩性的特点&#xff0c;是开发分布式系统和应用系统必备的技术之一。目前&#xff0c;针对不同的业务场…

游戏制作资源推荐

教程 创建僵尸第一人称射击游戏 | 虚幻引擎 5 初学者教程https://www.youtube.com/watch?vqOam3QjGE8g ​​​​​​​ 虚幻商城免费资产 人物资产 各种角色应有尽有 关键词&#xff1a;paragon &#xff1b;推荐程度&#xff1a;三颗星

STM32CubeMX学习笔记-USB接口使用(CDC虚拟串口)

STM32CubeMX学习笔记-USB接口使用&#xff08;CDC虚拟串口&#xff09; 一、USB简介二、新建工程1. 打开 STM32CubeMX 软件&#xff0c;点击“新建工程”2. 选择 MCU 和封装3. 配置时钟4. 配置调试模式 三、USB3.1 参数配置3.3 配置时钟3.4 USB Device 四、生成代码五、查看端口…

Go 基本数据类型和 string 类型介绍

Go 基础之基本数据类型 文章目录 Go 基础之基本数据类型一、整型1.1 平台无关整型1.1.1 基本概念1.1.2 分类有符号整型&#xff08;int8~int64&#xff09;无符号整型&#xff08;uint8~uint64&#xff09; 1.2 平台相关整型1.2.1 基本概念1.2.2 注意点1.2.3 获取三个类型在目标…