Medium
题目描述
给你一个整数数组 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 <= 10^5-10^4 <= nums[i] <= 10^4k的取值范围是[1, 数组中不相同的元素的个数]- 题目数据保证答案唯一,换句话说,数组中前
k个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组的大小。
解题思路
这道题有多种解法,我们来分析几种主要方法:
方法一:哈希表 + 最小堆(推荐)
首先统计每个元素的出现频率,然后使用大小为 k 的最小堆来维护前 k 个高频元素。当堆的大小超过 k 时,移除堆顶元素(频率最小的)。最后堆中剩下的就是前 k 个高频元素。
方法二:桶排序
统计频率后,使用桶排序的思想。创建一个数组,索引代表频率,值存储该频率对应的元素列表。由于最大频率不会超过数组长度,所以从后往前遍历桶,取出前 k 个元素即可。
方法三:快速选择
基于快速排序的分治思想,可以在平均 O(n) 时间内找到第 k 大的元素。先统计频率,然后对频率进行快速选择。
最小堆方法实现简单且稳定,时间复杂度 O(n log k),当 k 较小时性能很好,是最常用的解法。
代码实现
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> count;
for (int num : nums) {
count[num]++;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
for (auto& p : count) {
pq.push({p.second, p.first});
if (pq.size() > k) {
pq.pop();
}
}
vector<int> result;
while (!pq.empty()) {
result.push_back(pq.top().second);
pq.pop();
}
return result;
}
};
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
import heapq
from collections import Counter
count = Counter(nums)
heap = []
for num, freq in count.items():
heapq.heappush(heap, (freq, num))
if len(heap) > k:
heapq.heappop(heap)
return [num for freq, num in heap]
public class Solution {
public int[] TopKFrequent(int[] nums, int k) {
var count = new Dictionary<int, int>();
foreach (int num in nums) {
count[num] = count.GetValueOrDefault(num, 0) + 1;
}
var pq = new SortedSet<(int freq, int num)>();
foreach (var kvp in count) {
pq.Add((kvp.Value, kvp.Key));
if (pq.Count > k) {
pq.Remove(pq.Min);
}
}
return pq.Select(x => x.num).ToArray();
}
}
var topKFrequent = function(nums, k) {
const count = new Map();
for (const num of nums) {
count.set(num, (count.get(num) || 0) + 1);
}
const heap = [];
for (const [num, freq] of count) {
heap.push([freq, num]);
if (heap.length > k) {
heap.sort((a, b) => a[0] - b[0]);
heap.shift();
}
}
return heap.map(item => item[1]);
};
复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 最小堆 | O(n log k) | O(n + k) |
| 桶排序 | O(n) | O(n) |
| 快速选择 | O(n) 平均,O(n²) 最坏 | O(n) |
其中 n 为数组长度,k 为要求的元素个数。推荐使用最小堆方法,在 k 较小时效率很高且实现简单。
相关题目
. Word Frequency (Medium)
. Kth Largest Element in an Array (Medium)
. Sort Characters By Frequency (Medium)
. Top K Frequent Words (Medium)
. K Closest Points to Origin (Medium)
. Sort Features by Popularity (Medium)
. Sender With Largest Word Count (Medium)
. Most Frequent Even Element (Easy)
. Linked List Frequency (Easy)