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

题目

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_mapunordered_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,则弹出堆顶元素,保证堆的大小为kminHeap.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.xdnf.cn/news/148182.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka日志索引详解以及生产常见问题分析与总结

文章目录 1、Kafka的Log日志梳理1.1、Topic下的消息是如何存储的&#xff1f;1.1.1、 log文件追加记录所有消息1.1.2、 index和timeindex加速读取log消息日志。 1.2、文件清理机制1.2.1、如何判断哪些日志文件过期了1.2.2、过期的日志文件如何处理 1.3、Kafka的文件高效读写机制…

MySQL-MVCC(Multi-Version Concurrency Control)

MySQL-MVCC&#xff08;Multi-Version Concurrency Control&#xff09; MVCC&#xff08;多版本并发控制&#xff09;&#xff1a;为了解决数据库并发读写和数据一致性的问题&#xff0c;是一种思想&#xff0c;可以有多种实现方式。 核心思想&#xff1a;写入时创建行的新版…

【多任务案例:猫狗脸部定位与分类】

【猫狗脸部定位与识别】 1 引言2 损失函数3 The Oxford-IIIT Pet Dataset数据集4 数据预处理4 创建模型输入5 自定义数据集加载方式6 显示一批次数据7 创建定位模型8 模型训练9 绘制损失曲线10 模型保存与预测 1 引言 猫狗脸部定位与识别分为定位和识别&#xff0c;即定位猫狗…

MacOS怎么安装Nacos(附带:Windows系统)

MacOS安装Nacos&#xff08;一定要配置JDK的环境变量&#xff0c;后面告诉你为什么&#xff1f;&#xff09; &#xff08;1&#xff09;进入Nacos官网&#xff0c;前往githubhomehomehttp://nacos.io/zh-cn/ &#xff08;2&#xff09;点击右下角的releases 然后点击Tags 选择…

代码随想录算法训练营第五十七天 | 392.判断子序列 115.不同的子序列

1. 判断子序列 392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度。 class Solution {public boolean isSubsequence(String s, String t) {//dp[i][j] 表示…

Redis7的数据结构

Redis以键-值对的形式存储数据 一、键 1、键的特点 键是一个字符串&#xff0c;这个字符串的内容可以是数字、字符序列&#xff0c;也可以是一个文件的字节序列&#xff0c;甚至空字符串也可以做为key。 在一个数据库中键必须是唯一的。 键最大可以达到512M&#xff0c;但太…

通用收藏管理器Koillection

什么是 Koillection &#xff1f; Koillection 是一个自托管的收藏管理器&#xff0c;旨在跟踪任何类型的物理&#xff08;主要&#xff09;收藏&#xff0c;如书籍、DVD、邮票、游戏……&#xff0c;由于 Koillection 旨在用于任何类型的收藏&#xff0c;它不支持自动下载元数…

STM32 DMA从存储器发送数据到串口

1.任务描述 &#xff08;1&#xff09;ds18b20测量环境温度存储到存储器&#xff08;数组&#xff09;中。 &#xff08;2&#xff09;开启DMA将数组中的内容&#xff0c;通过DMA发送到串口 存在问题&#xff0c;ds18b20读到的数据是正常的&#xff0c;但是串口只是发送其低…

Redis最常见的5种应用场景

Redis作为当今最流行的内存数据库&#xff0c;已经成为服务端加速的必备工具之一。对于Redis为什么那么快&#xff1f;以及Redis采用单线程&#xff0c;但为什么反而获得更高的性能的疑问&#xff0c;在之前的Redis为什么那么快&#xff1f;一文中&#xff0c;已经有所介绍。 …

全新UI彩虹外链网盘系统源码(前后端美化模板)

全新UI彩虹外链网盘系统源码前后端美化模板&#xff0c;支持所有格式文件的上传、生成文件外链、图片外链、音乐视频外链等功能&#xff0c;同时还可以自动生成相应的 UBB 代码和 HTML 代码&#xff0c;支持文本、图片、音乐、视频在线预览。这不仅仅是一个网盘&#xff0c;更是…

证书显示未受信任,生成的证书过期

此时若是导入证书后&#xff0c;证书显示未受信任&#xff0c;则说明我们缺失最新的AppleWWDRCA证书 解决方案&#xff1a; 重新下载AppleWWDRCA并安装。即下载最新的AppleWWDRCA证书&#xff0c;双击安装到“登录”项的钥匙串下&#xff1b;然后再安装你的开发证书或者发布证书…

WebSocket实战之六心跳重连机制

一、前言 WebSocket应用部署到生产环境&#xff0c;我们除了会碰到因为经过代理服务器无法连接的问题&#xff08;注&#xff1a;该问题可以通过搭建WSS来解决&#xff0c;具体配置请看 WebSocket实战之四WSS配置 &#xff09;&#xff0c;另外一个问题就是外网环境不稳定经常…

Appium开发

特点 开源免费支持多个平台 IOS(苹果)、安卓App的自动化都支持 支持多种类型的自动化 支持苹果、安卓应用原生界面的自动化支持应用内嵌网络视图的自动化支持手机浏览器(Chrome)中的web网站自动化支持flutter应用的自动化 支持多种编程语言 像selenium一样&#xff0c;可以用多…

react.js在visual code 下的hello World

想学习reacr.js &#xff0c;就开始做一个hello world。 我的环境是visual code &#xff0c;所以我找这个环境下的例子。参照&#xff1a; https://code.visualstudio.com/docs/nodejs/reactjs-tutorial 要学习react.js &#xff0c;还得先安装node.js&#xff0c;我在visual …

面试打底稿④ 专业技能的第四部分

简历原文 抽查部分 了解Python的使用&#xff08;第一篇关于Python升级版本bug解决的文章斩获6W阅读&#xff09;&#xff0c;用python实现了几篇图像信息隐藏领 域论文的复现&#xff08;博客中有提及&#xff09;&#xff1b; 了解Django基本框架&#xff0c;写过Django框架的…

【软考】4.2 关系代数

《 关系代数 》 表和表之间的逻辑运算 笛卡尔积&#xff1a;S1 x S2 投影&#xff1a;π&#xff1b;选择某一列&#xff08;属性&#xff09;&#xff1b;一个关系R的投影操作结果也是一个关系&#xff0c;记作Πa&#xff0c;它由从关系R中选出的A列元素构成&#xff1b;选择…

测开 | Vue速查知识点

文章目录 Vue知识1. Vue 概述2. Vue 代码格式3. Vue 指令3.1 v-bind & v-model3.2 v-on3.3 v-if和v-show3.4 v-for 4. 生命周期 Vue知识 1. Vue 概述 简介&#xff1a; Vue.js&#xff08;读音 /vjuː/, 类似于 view&#xff09; 是一套构建用户界面的 渐进式框架。与其他…

CodeCraft-21 and Codeforces Round 711 (Div. 2)A-F

1.Problem - A - Codeforces &#xff08;1&#xff09;题意 求一个大于等于n的整数x&#xff0c;满足gcd(x,sum(dig(x)) > 1&#xff0c;dig表示x的各个数位。 &#xff08;2&#xff09;思路 考虑最差是满足gcd(x,sum(dig(x)) 2,因此不会枚举很多&#xff0c;直接暴力枚…

Webpack 基础入门以及接入 CSS、Typescript、Babel

一、什么是 Webpack Webpack 是一款 JS 模块化开发的技术框架&#xff0c;其运作原理是将多个 JS 文件关联起来构成可运行的应用程序。 Webpack 拥有丰富的 plugins / loaders 插件生态圈&#xff0c;可以让 js 识别不同的语言如 .css, .scss, .sass, .json, .xml, .ts, .vue…

数据结构:复杂度分析

目录 1 算法效率评估 1.1 实际测试 1.2 理论估算 2 迭代与递归 2.1 迭代 1. for 循环 2. while 循环 3. 嵌套循环 2.2 递归 1. 调用栈 2. 尾递归 3. 递归树 2.3 两者对比 3 时间复杂度 3.1 统计时间增长趋势 3.2 函数渐近上界…