跳到正文
第 347 题中等

前 K 个高频元素

Top K Frequent Elements

返回数组中出现频率最高的 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]

 

提示:

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

 

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

解题思路

主解法

频率表 + 桶排序

用出现次数当桶下标,从高到低取满 k 个

先统计每个元素的出现次数。频率的范围只有 1..n,所以可以开一个长度为 n+1 的桶数组,把元素放进“出现次数”对应的桶里。

分步图解

先统计频率,再按“出现次数”建桶;最后从高次数往低次数取满 2 个。

频率表 · 值 → 出现次数
{ }
桶 · 下标是出现次数(从高到低扫描)
[ ]
res
[ ]
1 / 4
频率表统计完后按次数建桶;桶下标就是出现次数。

然后从 n 向下扫描桶,先取高频率桶里的元素,取满 k 个就停止。这样不需要对所有不同元素排序,时间 O(n)。

自拟数组中 7 出现 4 次、5 出现 3 次,k=2,直接从 4 次桶和 3 次桶各取一个,得到 [7,5]。

分步图解

按次数放入桶:4 次桶放 7,3 次桶放 5,2 次桶放 2,1 次桶放 9。

频率表 · 值 → 出现次数
5→3
2→2
7→4
9→1
桶 · 下标是出现次数(从高到低扫描)
4次: 73次: 52次: 21次: 9
res
[ ]
1 / 4
从高次数桶向下扫描,取到 k 个立即停止,低次数桶不再访问。

桶排序的代价是需要 O(n) 的桶空间;如果不同元素很少、k 也很小,用小顶堆 O(n log k) 更省空间。

时间 O(n),空间 O(n)。

时间 O(n)空间 O(n)

易错点

  • 桶下标用元素值而不是频率:频率范围才是 1..n。
  • 从低频率往高扫描:会先取到最不频繁的元素。
  • 取满 k 个后不 break:可能多取元素。
  • 用 unordered_map 遍历顺序当答案顺序:题目不要求顺序,但调试时不要依赖它。

代码与演示

演示输入nums = [5,5,5,2,2,7,7,7,7,9],k = 2

先统计频率,再按“出现次数”建桶;最后从高次数往低次数取满 2 个。

频率表 · 值 → 出现次数
{ }
桶 · 下标是出现次数(从高到低扫描)
[ ]
res
[ ]
1 / 5
查看 C++ 代码

延伸阅读

  • “频率范围有限”是桶排序的关键条件;如果频率没有上界,就不能直接开数组。
  • 另一种常见写法是维护大小为 k 的小顶堆,按频率淘汰,时间 O(n log k)、空间 O(n)。