当前位置: 首页 > news >正文

力扣日记10.31-【栈与队列篇】前 K 个高频元素

力扣日记:【栈与队列篇】前 K 个高频元素

日期:2023.10.31
参考:代码随想录、力扣

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 是数组大小。

题解

class Solution {
#define SOLUTION 2
public:vector<int> topKFrequent(vector<int>& nums, int k) {
#if SOLUTION == 1// 时间复杂度: O(n) + O(n) + O(nlogn) + O(k) = O(nlogn)// 空间复杂度: O(n+n)unordered_map<int, int> cnt;for (const auto& n: nums) {cnt[n]++;}// 将哈希表的内容复制到 vector// 使用迭代器范围构造函数(iterator range constructor)创建 sortedVector// 这个构造函数接受两个迭代器,它会遍历 cnt 中的元素,然后复制它们到 sortedVector 中vector<pair<int, int>> sortedVector(cnt.begin(), cnt.end());// 按第二个值的大小对 vector 进行排序(从大到小)sort(sortedVector.begin(), sortedVector.end(), [](const pair<int, int>& a, const pair<int, int>& b) { // 匿名函数作为比较器参数return a.second > b.second; // 前者大于后者时返回true,表示前者应该在后者前面(大在前、小在后)});// 取前k个元素int count = 0;vector<int> result;for (const auto& pair : sortedVector) {result.push_back(pair.first);count++;if (count >= k) break;}return result;}
#elif SOLUTION == 2// unordered_map + 小顶堆// O(nlogk), O(n+k)// 之所以用堆,是因为没必要对n个元素都进行排序,只需要维护前k个元素即可// 1. 首先用map遍历一遍数组,确定每个数出现的频率unordered_map<int, int> cnt;for (const auto& n: nums) {cnt[n]++;}// 2. 使用小顶堆遍历map,维护出现频率最高的前k个元素// 小顶堆:本质是一个二叉树,每个父结点的值小于子结点,即根结点的值是最小的,值从小到大排列// 关于为什么用小顶堆而不是用大顶堆:/*如果是大顶堆的话,由于其仅维护k个元素,每次push进元素时都需要pop掉根结点元素而根结点是值最大的元素,这样的话会导致最后大顶堆中都是出现频率最低的前k个元素所以要用小顶堆,每次pop元素弹出值最小的元素,维护出现频率最高的前k个元素*/// 小顶堆通过优先级队列实现priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;// 用固定大小为k的小顶堆,扫面所有频率的数值for (unordered_map<int, int>::iterator it = cnt.begin(); it != cnt.end(); it++) {pri_que.push(*it); // 把it指向的<key,value>放进队列if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为kpri_que.pop();}}// 3. 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组vector<int> result(k);for (int i = k - 1; i >= 0; i--) {result[i] = pri_que.top().first;pri_que.pop();}return result;}// 小顶堆class mycomparison {public:bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {return lhs.second > rhs.second; // 为什么是>(大的在前,小的在后???)}};
#endif
};

复杂度

  • 哈希map + 快排:
    • 时间复杂度:O(nlogn)
    • 空间复杂度:O(n)
  • 哈希map + 小顶堆
    • 时间复杂度:O(nlogk)
    • 空间复杂度:O(n)

思路总结

  • 解法1:哈希map + 快排
    • 哈希map不能直接排序,要转换为vector才能进行排序
      • 1.将哈希表的内容复制到 vector
      • 2.使用迭代器范围构造函数(iterator range constructor)创建 sortedVector
        • 这个构造函数接受两个迭代器,它会遍历 cnt 中的元素,然后复制它们到 sortedVector 中
        • vector<pair<int, int>> sortedVector(cnt.begin(), cnt.end());
      • 3.按第二个值的大小对 vector 进行排序(从大到小)
      sort(sortedVector.begin(), sortedVector.end(), [](const pair<int, int>& a, const pair<int, int>& b) { // 匿名函数作为比较器参数return a.second > b.second; // 前者大于后者时返回true,表示前者应该在后者前面(大在前、小在后)});
      
  • 解法2:哈希map + 小顶堆
    • 之所以用小顶堆而不用快排,是因为没必要对n个元素都进行排序,只需要维护前k个元素即可(快排需要对n个元素进行排序,O(nlogn),小顶堆每次pop和push一个元素只需要logk,即对所有元素的总时间复杂度为O(nlogk)
    • 思路步骤:
    • 1.首先用map遍历一遍数组,确定每个数出现的频率
    • 2.使用小顶堆遍历map,维护出现频率最高的前k个元素
      • 小顶堆:本质是一个二叉树,每个父结点的值小于子结点,即根结点的值是最小的,值从小到大排列
      • 关于为什么用小顶堆而不是用大顶堆:
        如果是大顶堆的话,由于仅维护k个元素,每次push进元素时都需要pop掉根结点元素
        而根结点是值最大的元素,这样的话会导致最后大顶堆中都是出现频率最低的前k个元素
        所以要用小顶堆,每次pop元素弹出值最小的元素,维护出现频率最高的前k个元素
      • 小顶堆通过优先级队列实现:其中比较器设置为"左值>右值"(可能与优先级队列的底层实现有关)—— 注意这与快排cmp是相反的,快排cmp“左值>右值"是从大到小降序排列,而优先级队列"左值>右值"是小顶堆(根小子大)
    • 3.找出前K个高频元素,因为小顶堆先弹出的是最小的(取first即元素的键),所以倒序来输出到数组
  • 学会小顶堆的实现以及小顶堆遍历的写法:
// 小顶堆实现
class mycomparison {
public:bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {return lhs.second > rhs.second;}
};
// 优先级队列定义与遍历
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
// 参数1:优先级队列的元素类型,参数2:优先级队列自身类型,参数3:优先级队列的比较器(决定是小顶堆还是大顶堆)
for (unordered_map<int, int>::iterator it = cnt.begin(); it != cnt.end(); it++) {pri_que.push(*it); // 把it指向的<key,value>放进队列if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为kpri_que.pop();}
}

相关文章:

力扣日记10.31-【栈与队列篇】前 K 个高频元素

力扣日记&#xff1a;【栈与队列篇】前 K 个高频元素 日期&#xff1a;2023.10.31 参考&#xff1a;代码随想录、力扣 347. 前 K 个高频元素 题目描述 难度&#xff1a;中等 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意…...

TensorFlow案例学习:简单的音频识别

前言 以下内容均来源于官方教程&#xff1a;简单的音频识别&#xff1a;识别关键字 音频识别 下载数据集 下载地址&#xff1a;http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip 可以直接浏览器访问下载。 下载完成后将其解压到项目…...

css小程序踩坑记录

写标签设置距离 一直设置不动 写个双层 设置动了 神奇 好玩...

Android sqlite分页上传离线订单后删除

1、判断订单表的的总数是否大于0&#xff0c;如果大于0开始上传订单 public int getOrderCount() {String query "SELECT COUNT(*) FROM " TABLE_NAME;Cursor cursor db.rawQuery(query, null);int count 0;if (cursor.moveToFirst()) {count cursor.getInt(0);…...

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧&#xff0c;非常容易上手&#xff1a; # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK&#xff1a;欢迎访问主页&#xff01;"if __name__ "__main__"…...

Django实战项目-学习任务系统-兑换物品管理

接着上期代码框架&#xff0c;开发第5个功能&#xff0c;兑换物品管理&#xff0c;再增加一个学习兑换物品表&#xff0c;主要用来维护兑换物品&#xff0c;所需积分&#xff0c;物品状态等信息&#xff0c;还有一个积分流水表&#xff0c;完成任务奖励积分&#xff0c;兑换物品…...

jmeter和postman你选哪个做接口测试?

软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大部分人在做的接口测试&#xff0c;小白变高手…...

mac版本 Adobe总是弹窗提示验证问题如何解决

来自&#xff1a; mac软件下载macsc站 mac电脑使用过程中总是弹出Adobe 的弹窗提示&#xff0c;尤其是打开Adobe的软件&#xff0c;更是频繁的弹出提示&#xff1a; Your Adobe app is not genuine. Adobe reserves the right to disable this software after a 0 grace period…...

钡铼技术ARM工控机在机器人控制领域的应用

ARM工控机是一种基于ARM架构的工业控制计算机&#xff0c;用于在工业自动化领域中进行数据采集、监控、控制和通信等应用。ARM&#xff08;Advanced RISC Machine&#xff09;架构是一种低功耗、高性能的处理器架构&#xff0c;广泛应用于移动设备、嵌入式系统和物联网等领域。…...

HTML+CSS+JS实现计算器

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…...

Git工作原理和常见问题处理方案

博客定位Git工作区域工作区域划分暂存区设计目的 Git基本操作核心操作初始化和配置指令 HEAD指针Git版本回滚指令介绍reset模式reset hard使用场景reset soft使用场景reset mixed使用场景reset使用注意事项checkout使用场景 Git分支管理什么是分支分支应用场景分支相关指令被合…...

C++-实现一个简单的菜单程序

C-实现一个简单的菜单程序 1&#xff0c;if-else语句实现1.1&#xff0c;代码实现1.2&#xff0c;功能检测 2&#xff0c;switch语句实现2.1&#xff0c;代码实现2.2&#xff0c;功能检测 1&#xff0c;if-else语句实现 实现一个简单的菜单程序&#xff0c;运行时显示"Men…...

Git更新 fork 的仓库

文章目录 确保本地仓库是最新的配置上游存储库(remote upstream)获取上游存储库的更改合并上游存储库的更改推送更改到你 fork 的仓库 确保本地仓库是最新的 在命令行中&#xff0c;导航到存储库的本地副本所在的目录&#xff0c;并执行以下命令&#xff1a; # 切换到主分支 …...

chorme安装esay scholar及chrome 无法从该网站添加应用、扩展程序和用户脚本解决方案

问题描述 如题&#xff0c;博主想安装easy scholar用于查询论文的分区&#xff0c;结果安装了半天一直出现chrome 无法从该网站添加应用、扩展程序和用户脚本解决方案的问题。 解决方案 先从这个网址下载&#xff1a;https://www.easyscholar.cc/download 然后对下载好的文…...

数据库-扩展语句,约束方式

扩展语句&#xff1a; 例&#xff1a; 自增长&#xff1a; auto_increment:表示该字段可以自增长&#xff0c;默认从一开始&#xff0c;每条记录会自动递增1 复制&#xff1a; 通过like这个语法直接复制ky32的表结构&#xff0c;只能复制表结构&#xff0c;不能复制表里面的…...

精密数据工匠:探索 Netty ChannelHandler 的奥秘

通过上篇文章&#xff08;Netty入门 — Channel&#xff0c;把握 Netty 通信的命门&#xff09;&#xff0c;我们知道 Channel 是传输数据的通道&#xff0c;但是有了数据&#xff0c;也有数据通道&#xff0c;没有数据加工也是没有意义的&#xff0c;所以今天学习 Netty 的第四…...

Python四种基本结构的操作

列表 列表的创建有两种方法 SampleList [] SampleList list() 列表中元素的添加 append(obj)&#xff1a;在列表末尾添加元素obj extend(seq)&#xff1a;在列表末尾添加多个值&#xff0c;使用extend()函数&#xff0c;seq是一个可迭代对象&#xff0c;否则报错。 Inser…...

Eureka:com.netflix.discovery.TimedSupervisorTask - task supervisor timed out

1、原因是spring cloud netflix中&#xff0c;某个服务挂掉了或者是执行某个任务时间过长&#xff0c;而没有发送给Eureka心跳 &#xff0c;导致调用不到指定的服务&#xff0c;所以检查被调用服务器是否有问题。 2、有可能是某一个微服务自身内部G了&#xff0c;导致没有向eu…...

1.spark standalone环境安装

概述 环境是spark 3.2.4 hadoop版本 3.2.4&#xff0c;所以官网下载的包为 spark-3.2.4-bin-hadoop3.2.tgz 在具体安装部署之前&#xff0c;需要先下载Spark的安装包&#xff0c;进到 spark的官网&#xff0c;点击download按钮 使用Spark的时候一般都是需要和Hadoop交互的&a…...

【问题解决】 avue dicUrl 动态参数加载字典数据(已解决)

事情是这样的&#xff0c;用了avue-crud组件&#xff0c;配置了一个option。     现在有一列source属性要展示为 多选的下拉框 &#xff0c;当然问题不在这而在于&#xff0c;选项是需要根据同级别属性id去拿的。也就是option.column.source 的配置中 需要该行的option.col…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

云原生周刊:k0s 成为 CNCF 沙箱项目

开源项目推荐 HAMi HAMi&#xff08;原名 k8s‑vGPU‑scheduler&#xff09;是一款 CNCF Sandbox 级别的开源 K8s 中间件&#xff0c;通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度&#xff0c;为容器提供统一接口&#xff0c;实现细粒度资源配额…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

用递归算法解锁「子集」问题 —— LeetCode 78题解析

文章目录 一、题目介绍二、递归思路详解&#xff1a;从决策树开始理解三、解法一&#xff1a;二叉决策树 DFS四、解法二&#xff1a;组合式回溯写法&#xff08;推荐&#xff09;五、解法对比 递归算法是编程中一种非常强大且常见的思想&#xff0c;它能够优雅地解决很多复杂的…...

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…...