news 2026/6/16 16:31:49

【力扣100题】92.前 K 个高频元素

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【力扣100题】92.前 K 个高频元素

题目描述

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

示例 1:

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

示例 2:

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

示例 3:

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

解题思路总览

思路时间复杂度空间复杂度适用场景
排序后统计O(n log n)O(n)简单直接,容易想到
堆排序O(n log k)O(n)TopK 经典解法
桶排序O(n)O(n)本题最优解
快速选择O(n)O(n)需要部分排序时

算法一:排序后统计

思路

统计每个元素的出现频率,然后对频率进行排序。

代码

classSolution{public:vector<int>topKFrequent(vector<int>&nums,intk){unordered_map<int,int>cnt;for(intnum:nums){cnt[num]++;}// 将 map 转为 vector 并按频率降序排序vector<pair<int,int>>vec(cnt.begin(),cnt.end());sort(vec.begin(),vec.end(),[](constpair<int,int>&a,constpair<int,int>&b){returna.second>b.second;});vector<int>ans;for(inti=0;i<k;i++){ans.push_back(vec[i].first);}returnans;}};

算法流程

输入: nums = [1,1,1,2,2,3], k = 2 第一步:统计频率 +-------------------------------------------------------------+ | num | 1 | 2 | 3 | | | cnt | 3 | 2 | 1 | | +-------------------------------------------------------------+ 第二步:转成 vector 并排序 +-------------------------------------------------------------+ | vec = [(1,3), (2,2), (3,1)] --按 second 降序--> [(1,3),(2,2),(3,1)] | +-------------------------------------------------------------+ 第三步:取前 k 个 +-------------------------------------------------------------+ | 取前 2 个: ans = [1, 2] | +-------------------------------------------------------------+ 输出: [1,2]

复杂度分析

复杂度分析
时间复杂度O(n log n)
- 统计频率:O(n)
- 排序 n 个不同元素:O(n log n)
空间复杂度O(n)
-cnt哈希表:O(n)
-vec数组:O(n)

算法二:堆排序(TopK 经典解法)

思路

维护一个大小为 k 的小根堆,遍历频率数组,保留出现频率最高的 k 个元素。

小根堆的性质:堆顶是最小元素,每次插入新元素时,会把最小元素"挤"到堆顶,堆大小超过 k 时弹出。

代码

classSolution{public:vector<int>topKFrequent(vector<int>&nums,intk){unordered_map<int,int>cnt;for(intnum:nums){cnt[num]++;}// 小根堆:pair(频率, 元素),频率小的在堆顶priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq;for(auto&[num,freq]:cnt){pq.push({freq,num});if(pq.size()>k){pq.pop();// 弹出频率最小的}}vector<int>ans;while(!pq.empty()){ans.push_back(pq.top().second);pq.pop();}returnans;}};

算法流程

输入: nums = [1,1,1,2,2,3], k = 2 第一步:统计频率 +-------------------------------------------------------------+ | num | 1 | 2 | 3 | | | cnt | 3 | 2 | 1 | | +-------------------------------------------------------------+ 第二步:遍历哈希表,入堆并维护大小为 k 的小根堆 +-------------------------------------------------------------+ | 遍历 (1,3): pq=[(3,1)], size=1 <= k,不弹出 | | 遍历 (2,2): pq=[(2,2),(3,1)], size=2 <= k,不弹出 | | 遍历 (3,1): pq=[(1,3),(3,1),(2,2)], size=3 > k,弹出堆顶 | | 弹出 (1,3) --> pq=[(2,2),(3,1)] | +-------------------------------------------------------------+ 第三步:取出堆中所有元素 +-------------------------------------------------------------+ | ans = [2, 1] (顺序无关) | +-------------------------------------------------------------+ 输出: [1,2]

复杂度分析

复杂度分析
时间复杂度O(n log k)
- 统计频率:O(n)
- 遍历 n 个不同元素,每次入堆 O(log k),最多 n 次
- 弹出 n-k 次,每次 O(log k)
空间复杂度O(n)
-cnt哈希表:O(n)
- 堆最多 k 个元素:O(k)

算法三:桶排序(本题最优解)

思路

出现次数的范围是 [1, n],直接用下标对应频率,创建桶数组。桶的下标表示出现次数,每个桶里存放出现该次数的所有元素。从高频率向低频率收集。

代码

classSolution{public:vector<int>topKFrequent(vector<int>&nums,intk){unordered_map<int,int>cnt;intmax_cnt=0;for(intnum:nums){cnt[num]++;max_cnt=max(max_cnt,cnt[num]);}vector<vector<int>>buckets(max_cnt+1);for(auto&[x,c]:cnt){buckets[c].push_back(x);}vector<int>ans;for(inti=max_cnt;ans.size()<k;i--){ans.insert(ans.end(),buckets[i].begin(),buckets[i].end());}returnans;}};

算法流程

输入: nums = [1,1,1,2,2,3], k = 2 第一步:统计频率 +-------------------------------------------------------------+ | num | 1 | 2 | 3 | | | cnt | 3 | 2 | 1 | | | max_cnt = 3 | +-------------------------------------------------------------+ 第二步:创建桶并放入元素(桶编号 = 出现次数) +-------------------------------------------------------------+ | bucket[0] | bucket[1] | bucket[2] | bucket[3] | | | [] | [3] | [2] | [1] | | +-------------------------------------------------------------+ 第三步:从高到低遍历桶,收集 k 个元素 +-------------------------------------------------------------+ | i = 3: bucket[3] = [1] --> ans = [1] | | i = 2: bucket[2] = [2] --> ans = [1, 2] | | ans.size() = 2 == k,停止遍历 | +-------------------------------------------------------------+ 输出: [1,2]

复杂度分析

复杂度分析
时间复杂度O(n)
- 统计频率:O(n)
- 入桶:O(n)
- 遍历桶:最坏 O(n)(所有元素在同一桶)
空间复杂度O(n)
-cnt哈希表:O(n)
-buckets桶数组:O(n)

算法四:快速选择(基于 Partition)

思路

利用快速排序的 Partition 思想。先统计频率,然后基于频率对元素进行划分。第 n-k 个位置(从小到大第 k 个)的左边都是频率大于等于它的元素。

代码

classSolution{public:vector<int>topKFrequent(vector<int>&nums,intk){unordered_map<int,int>cnt;for(intnum:nums){cnt[num]++;}// 将哈希表转为 vectorvector<pair<int,int>>vec;for(auto&[num,freq]:cnt){vec.push_back({num,freq});}intn=vec.size();inttarget=n-k;// 要找到第 target 小的元素// 快速选择random_shuffle(vec.begin(),vec.end());intleft=0,right=n-1;while(left<=right){intpivot=partition(vec,left,right);if(pivot==target){break;}elseif(pivot<target){left=pivot+1;}else{right=pivot-1;}}vector<int>ans;for(inti=target;i<n;i++){ans.push_back(vec[i].first);}returnans;}private:intpartition(vector<pair<int,int>>&vec,intleft,intright){intpivot_freq=vec[right].second;inti=left;for(intj=left;j<right;j++){if(vec[j].second<pivot_freq){swap(vec[i],vec[j]);i++;}}swap(vec[i],vec[right]);returni;}};

算法流程

输入: nums = [1,1,1,2,2,3], k = 2 第一步:统计频率 +-------------------------------------------------------------+ | vec = [(1,3), (2,2), (3,1)] | | n = 3, target = 3 - 2 = 1 | +-------------------------------------------------------------+ 第二步:快速选择 Partition +-------------------------------------------------------------+ | 初始: vec = [(1,3), (2,2), (3,1)], left=0, right=2 | | pivot = vec[2] = (3,1), i=0 | | j=0: vec[0]=(1,3) >= pivot, 不动 | | j=1: vec[1]=(2,2) >= pivot, 不动 | | 交换 vec[0] 和 vec[2]: vec = [(3,1), (2,2), (1,3)] | | 返回 i=0 | | pivot=0 < target=1, left=1 | +-------------------------------------------------------------+ 第三步:继续 Partition +-------------------------------------------------------------+ | vec = [(3,1), (2,2), (1,3)], left=1, right=2 | | pivot = vec[2] = (1,3), i=1 | | j=1: vec[1]=(2,2) >= pivot, 不动 | | 交换 vec[1] 和 vec[2]: vec = [(3,1), (1,3), (2,2)] | | 返回 i=1 | | pivot=1 == target=1, 停止 | +-------------------------------------------------------------+ 第四步:取第 target=1 之后的元素 +-------------------------------------------------------------+ | ans = [(1,3), (2,2)] 的 first --> [1, 2] | +-------------------------------------------------------------+ 输出: [1,2]

复杂度分析

复杂度分析
时间复杂度O(n)(平均情况)
- 统计频率:O(n)
- 每次 Partition O(n),递归深度 O(log n)
- 最坏情况 O(n^2)(数据有序时)
空间复杂度O(n)
-cnt哈希表:O(n)
-vec数组:O(n)
- 递归栈深度 O(log n)

复杂度对比总结

思路平均时间最坏时间空间稳定性
排序后统计O(n log n)O(n log n)O(n)稳定
堆排序O(n log k)O(n log k)O(n)不稳定
桶排序O(n)O(n)O(n)不稳定
快速选择O(n)O(n^2)O(n)不稳定

各算法适用场景

算法适用场景
排序后统计数据量小,简单直接,容易实现
堆排序数据量大,k 较小,TopK 经典场景
桶排序频率范围确定,本题最优
快速选择需要部分排序,不需要维护额外数据结构

面试追问 FAQ

问题回答
为什么用unordered_map而不是mapunordered_map平均 O(1),map需要 O(log n)
堆排序为什么用小根堆而不是大根堆?小根堆堆顶最小,插入 O(log k),弹出后仍满足堆性质
桶排序适用于什么场景?数据范围确定且相对集中时,如频率统计、计数排序
快速选择最坏情况如何避免?随机打乱数组,或用三数取中选 pivot
如果 k=1 但有多个频率相同的元素怎么办?返回任意一个都可以,题目允许任意顺序
如何处理负数?unordered_map原生支持负数键,不影响

相关题目

题目难度核心点
692. 前K个高频单词中等多元素比较规则,需按字典序二次排序
703. 数据流中的第K大元素简单堆的经典应用,数据流场景
215. 数组中的第K个最大元素中等QuickSelect 或堆排序
347. 前K个高频元素中等桶排序最优,频率统计

总结

项目内容
最优解桶排序 O(n) 时间、O(n) 空间
核心思想用桶下标直接对应出现次数,省去堆
关键技巧频率范围 [1, n] 确定,可用下标直接寻址
面试建议优先给出桶排序,能想到堆排序是加分项
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 2:16:57

世界杯还没结束,但AI已经把创意玩疯了

每届世界杯都会诞生很多经典画面。 绝杀。 逆转。 欢呼。 泪水。 但今年除了赛场上的比赛之外&#xff0c;还有另一场有趣的“世界杯”。 那就是&#xff1a; AI创意世界杯。 最近我发现一件很有意思的事情。 很多人已经不满足于单纯看比赛了。 他们开始用AI创造属于自…

作者头像 李华
网站建设 2026/6/15 2:15:52

避开这三个坑,你的AUV Simulink运动仿真才算入门(附PD控制模型文件)

避开这三个坑&#xff0c;你的AUV Simulink运动仿真才算入门水下自主航行器&#xff08;AUV&#xff09;的运动控制仿真一直是机器人领域的热门研究方向。许多工程师和研究人员在Simulink中搭建控制回路时&#xff0c;常常遇到仿真结果不理想的情况——系统发散、超调过大或是根…

作者头像 李华
网站建设 2026/6/16 4:40:58

从PEEQ警告到单元扭曲:一次ABAQUS弹塑性分析不收敛的完整排错复盘

从PEEQ警告到单元扭曲&#xff1a;一次ABAQUS弹塑性分析不收敛的完整排错复盘当你盯着ABAQUS Job Monitor里不断闪烁的黄色警告标志&#xff0c;MSG文件中密密麻麻的PEEQ和负特征值警告&#xff0c;以及后处理中那些扭曲变形的单元网格时&#xff0c;是否感到一阵无力&#xff…

作者头像 李华
网站建设 2026/6/15 2:06:50

CANoe CAPL编程避坑指南:关于Message声明与发送,新手最常踩的3个雷

CANoe CAPL编程避坑指南&#xff1a;关于Message声明与发送&#xff0c;新手最常踩的3个雷在汽车电子开发领域&#xff0c;CANoe作为主流的总线分析工具&#xff0c;其CAPL编程能力是工程师必须掌握的技能。然而&#xff0c;许多初学者在Message声明与发送环节频频踩坑&#xf…

作者头像 李华